Commit Graph
29 Commits
Author SHA1 Message Date
Jonatan Männchen 980500551c Inline Licence Info & Copyright for non-test files (#14256) 2025-02-07 21:11:09 +01:00
José Valim 768fa4963e Optimize inspection of binaries
We have used this code:

    data = Base.encode64(:crypto.strong_rand_bytes(2_000_000))

    Mix.Tasks.Profile.Tprof.profile(
      fn -> inspect(data, printable_limit: :infinity) end,
      type: :memory
    )

The first result reported this:

    Profile results of #PID<0.106.0>
    #                                    CALLS      %      WORDS PER CALL
    Total                             10666685 100.00 9268308211   868.90
    :code.ensure_loaded/1                    1   0.00          3     3.00
    Inspect.BitString.inspect/2              1   0.00          4     4.00
    Inspect.Algebra.group/2                  1   0.00          4     4.00
    Inspect.Algebra.format/2                 1   0.00          6     6.00
    Inspect.Algebra.format/3                 3   0.00          8     2.67
    :erlang.iolist_to_binary/1               2   0.00         11     5.50
    anonymous fn/2 in Kernel.struct/2        1   0.00         17    17.00
    Inspect.Opts.new/1                       1   0.00         17    17.00
    Code.Identifier.escape/5           2666669   0.06    5333344     2.00
    String.recur_printable?/2          2666669   0.29   26666778    10.00
    Code.Identifier.escape_map/1       2666668  28.72 2662035405   998.26
    Code.Identifier.escape_char/1      2666668  70.93 6574272614  2465.35

As you can see, most of the allocations were in escape_char,
which makes sense: it will be invoked a lot for raw binaries
and it allocated lists, instead of relying on binary optimizations.

We reimplemented Code.Identifier.escape/5 to rely on binary
optimizations which then yielded this:

    Profile results of #PID<0.112.0>
    #                                   CALLS      %    WORDS PER CALL
    Total                             5333350 100.00 26666864     5.00
    :code.ensure_loaded/1                   1   0.00        3     3.00
    Inspect.BitString.inspect/2             1   0.00        4     4.00
    Inspect.Algebra.group/2                 1   0.00        4     4.00
    Inspect.Algebra.format/2                1   0.00        6     6.00
    Code.Identifier.escape/4                1   0.00        8     8.00
    Code.Identifier.escape/5          2666669   0.00        8     0.00
    Inspect.Algebra.format/3                3   0.00        8     2.67
    :erlang.iolist_to_binary/1              2   0.00       11     5.50
    anonymous fn/2 in Kernel.struct/2       1   0.00       17    17.00
    Inspect.Opts.new/1                      1   0.00       17    17.00
    String.recur_printable?/2         2666669 100.00 26666778    10.00

Now the allocations are all on `recur_printable?/2`. By running this
command, we can find out why it is allocating so much:

    ERL_COMPILER_OPTIONS=bin_opt_info elixir lib/elixir/lib/string.ex

It happens that, due to a catch all, new binary contexts had to be
allocated. By addressing that, we now get:

    Profile results of #PID<0.119.0>
    #                                   CALLS      % WORDS PER CALL
    Total                             5333350 100.00    91     0.00
    :code.ensure_loaded/1                   1   3.30     3     3.00
    Inspect.BitString.inspect/2             1   4.40     4     4.00
    Inspect.Algebra.group/2                 1   4.40     4     4.00
    String.recur_printable?/2         2666669   5.49     5     0.00
    Inspect.Algebra.format/2                1   6.59     6     6.00
    Code.Identifier.escape/4                1   8.79     8     8.00
    Code.Identifier.escape/5          2666669   8.79     8     0.00
    Inspect.Algebra.format/3                3   8.79     8     2.67
    :erlang.iolist_to_binary/1              2  12.09    11     5.50
    anonymous fn/2 in Kernel.struct/2       1  18.68    17    17.00
    Inspect.Opts.new/1                      1  18.68    17    17.00

Which is several orders of magnitude fewer words. We basically
allocate the new escaped binary and a few extra bytes.

Closes #14029.
2024-12-03 18:34:59 +01:00
Jean Klingler 0793fc91b9 Inspect special purpose chars using their unicode representation (#13676)
This reduces confusion when working with zero-width characters or alternative spaces.

Relates to #13673
2024-06-19 16:05:17 +09:00
José Valim 6655d40376 Streamline and document the parser 2024-02-07 15:06:48 +01:00
José Valim d68c8d6cdd Unify handling of .. and ... 2024-02-07 12:26:39 +01:00
sabiwara 29bf449f3f Evaluate formating charlists as ~c sigils (#12064) 2022-08-10 06:47:00 +02:00
José Valim a74d0cc390 Deprecate <|> 2021-12-13 20:13:03 +01:00
José Valim f1b9d3e818 Deprecate use Bitwise and ~~~
Use `import` instead.
2021-12-09 21:08:57 +01:00
José Valim 0a972503ec Add Macro.classify_atom/1 and Macro.inspect_atom/2 (#11446)
Today the formatter still uses some private APIs
and this commit aims to expose some of them.
In particular, Macro.classify_atom/1 was added to
expose if an atom is an alias, an identifier, quoted,
or unquoted.

Note that the API does not say anything about
operators, since they can fall within three distinct
categories:

  * unquoted and callable, such as, `+`, `-`, and
    most operators

  * unquoted and not callable, such as, `.`, `..`,
    and `..//`, which would be ambiguous when used
    as `Module.OP()`

  * quoted but callable, such as `::`, which is
    ambiguous in the atom syntax but not as calls

This information still remains private, especially
because most times operators are used, we often still
want to learn about their precedence too.

We also exposed `inspect_atom/2` with three
distinct clauses to cover the different scenarios
atoms can be inspected at runtime and in source
code.
2021-12-05 15:02:06 +01:00
Eksperimental b38db8e64b Fix spec for Code.Identifier.escape/4 (#11445)
Closes #11444
2021-12-05 10:51:52 +01:00
Eksperimental 9e50637364 Add guards and specs to Identifier.escape/4 (#11439) 2021-12-04 15:35:05 +01:00
José Valim 7a9a00d9e2 Add power operator (#11241) 2021-09-14 11:08:34 +02:00
José Valim 3c45984d2c Assume Elixir is not started when accessing configs 2021-05-21 12:02:15 +02:00
José Valim dcd0b6d4d1 Use persistent term for tokenizer and bootstrap 2021-04-01 10:45:15 +02:00
Eksperimental 4d31a8cf28 Format source code with new rules 2021-04-01 09:46:47 +02:00
José Valim b849235472 Support stepped ranges (#10810) 2021-03-25 07:32:06 +01:00
Ivan Rublev 0ae5f4fda5 Add +++ and --- with right associativity to the list of custom operators (#9954) 2020-04-21 13:03:31 +02:00
José Valim 8fac36f961 Quote :: to avoid ambiguity (#8890) 2019-03-14 17:08:04 +01:00
José Valim 10506c09d0 Clean up private elixir_config API 2018-07-19 23:27:52 +02:00
Andrea Leopardi 2a396cf8e0 Add a bunch of backticks to some docs in Code.Identifier
[ci skip]
2018-02-19 14:05:30 -08:00
José Valim d222b98971 Fix precedence of & in regards to =, closes #7188 2018-01-09 14:13:45 +01:00
Donald Little 3c7b65eae4 Show UTF-8 BOM on inspected strings (#7085) 2017-12-03 14:04:52 +01:00
Eric Meadows-Jönsson 87bf050e2c Fix precedence of bitwise XOR (^^^) in formatter (#7036)
Closes #7034.
2017-11-15 19:53:20 +01:00
José Valim be47d06764 Inline expressions when possible 2017-10-31 10:53:14 +01:00
José Valim 3fba28e0ee Treat => and : as operators in the formatter
This means that the right side will break on its own line
when it doesn't fit and be indented by two spaces.

Closes #6976.
2017-10-26 12:43:02 +02:00
José Valim 88b6e54671 Properly render :-> as an atom 2017-10-09 22:55:07 +02:00
José Valim d3ac3221ae Move & up in the operator table to avoid conflicts with | in cons, closes #6613 2017-09-28 18:32:52 +02:00
José Valim 1eb8985a81 Split callable in two types 2017-08-24 23:43:14 +02:00
José Valim d24cad38fb Add Code.Identifier to hold shared identifier logic throughout Elixir 2017-08-22 13:22:26 +02:00