Skip to content

JIT: Optimize creation of binaries - #6031

Merged
bjorng merged 3 commits into
erlang:masterfrom
bjorng:bjorn/jit/bs_create_bin
Jun 8, 2022
Merged

JIT: Optimize creation of binaries#6031
bjorng merged 3 commits into
erlang:masterfrom
bjorng:bjorn/jit/bs_create_bin

Conversation

@bjorng

@bjorng bjorng commented May 30, 2022

Copy link
Copy Markdown
Contributor

In the JIT, improve the code generation for binary construction for segments of fixed widths of no more than 64 bits and for variable width segments that are a set to 0.

I have run the benchmark linked to from #5639.

On my Intel iMac from 2017, the result without this pull request was:

    == Testing with 1 MB ==
    fun base64:encode/1: 1000 iterations in 16264 ms: 61 it/sec
    fun base64:decode/1: 1000 iterations in 18597 ms: 53 it/sec

With this pull request:

    == Testing with 1 MB ==
    fun base64:encode/1: 1000 iterations in 10955 ms: 91 it/sec
    fun base64:decode/1: 1000 iterations in 10629 ms: 94 it/sec

On my M1 MacBook Pro from 2020 the result before was:

    == Testing with 1 MB ==
    fun base64:encode/1: 1000 iterations in 12299 ms: 81 it/sec
    fun base64:decode/1: 1000 iterations in 15147 ms: 66 it/sec

After:

    == Testing with 1 MB ==
    fun base64:encode/1: 1000 iterations in 8550 ms: 116 it/sec
    fun base64:decode/1: 1000 iterations in 10066 ms: 99 it/sec

bjorng added 2 commits May 30, 2022 06:53
While at it, also update the comments, removing mentions of
old and new instruction sets. (The old instruction set was removed
a long time ago.)
Useful for the JIT because it has fewer arguments and thus can be
called using fewer instructions.
@bjorng bjorng added team:VM Assigned to OTP team VM enhancement testing currently being tested, tag is used by OTP internal CI labels May 30, 2022
@bjorng
bjorng requested review from garazdawi and jhogberg May 30, 2022 06:02
@bjorng bjorng self-assigned this May 30, 2022
@github-actions

github-actions Bot commented May 30, 2022

Copy link
Copy Markdown
Contributor

CT Test Results

       3 files     125 suites   37m 44s ⏱️
1 451 tests 1 409 ✔️ 42 💤 0
1 751 runs  1 692 ✔️ 59 💤 0

Results for commit e5bd2c1.

♻️ This comment has been updated with latest results.

To speed up review, make sure that you have read Contributing to Erlang/OTP and that all checks pass.

See the TESTING and DEVELOPMENT HowTo guides for details about how to run test locally.

Artifacts

// Erlang/OTP Github Action Bot

@bjorng
bjorng force-pushed the bjorn/jit/bs_create_bin branch from 8bf949d to 442cbff Compare May 30, 2022 07:11
Comment thread erts/emulator/beam/jit/arm/instr_bs.cpp Outdated
Comment thread erts/emulator/beam/jit/arm/instr_bs.cpp Outdated
Comment thread erts/emulator/beam/jit/x86/instr_bs.cpp Outdated
Comment thread erts/emulator/beam/jit/x86/instr_bs.cpp Outdated
Comment thread erts/emulator/beam/jit/x86/instr_bs.cpp Outdated
Comment thread erts/emulator/beam/jit/x86/instr_bs.cpp Outdated
Comment thread erts/emulator/beam/jit/arm/instr_bs.cpp Outdated
@bjorng
bjorng force-pushed the bjorn/jit/bs_create_bin branch 8 times, most recently from 845d29a to 69edb96 Compare June 2, 2022 12:07
Comment thread erts/emulator/beam/jit/arm/instr_bs.cpp Outdated
Comment thread erts/emulator/beam/jit/arm/instr_bs.cpp Outdated
Comment thread erts/emulator/beam/jit/arm/instr_bs.cpp Outdated
@bjorng
bjorng force-pushed the bjorn/jit/bs_create_bin branch 4 times, most recently from 06f4a21 to afd33b8 Compare June 7, 2022 05:25
@bjorng bjorng added this to the OTP-26.0 milestone Jun 7, 2022
Comment thread erts/emulator/beam/jit/x86/instr_bs.cpp Outdated
mov_imm(ARG3, seg.effectiveSize);
} else if (seg.unit == 0) {
/* Silly but legal. */
mov_imm(ARG3, 0);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to gcov, this line is never run. Is that because the compiler will never emit such code?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Contrary to the comment, a unit size of zero is not legal (at least for integer segments). I have removed the code.

Comment thread erts/emulator/beam/jit/x86/instr_bs.cpp Outdated

Uint error_info;
Sint effectiveSize;
enum class action { NONE, ACCUMULATE_FIRST, ACCUMULATE, STORE } action;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind adding a small description what the different actions do and when they trigger?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have renamed NONE to DIRECT and added more comments.

@bjorng
bjorng force-pushed the bjorn/jit/bs_create_bin branch 2 times, most recently from 199ada3 to e5bd2c1 Compare June 7, 2022 12:48
In the JIT, improve the code generation for binary construction
for segments of fixed widths of no more than 64 bits and for
variable width segments that are a set to 0.

I have run the benchmark linked to from erlang#5639.

On my Intel iMac from 2017, the result without this commit was:

    == Testing with 1 MB ==
    fun base64:encode/1: 1000 iterations in 16264 ms: 61 it/sec
    fun base64:decode/1: 1000 iterations in 18597 ms: 53 it/sec

With this commit:

    == Testing with 1 MB ==
    fun base64:encode/1: 1000 iterations in 10955 ms: 91 it/sec
    fun base64:decode/1: 1000 iterations in 10629 ms: 94 it/sec

On my M1 MacBook Pro from 2020 the result before was:

    == Testing with 1 MB ==
    fun base64:encode/1: 1000 iterations in 12299 ms: 81 it/sec
    fun base64:decode/1: 1000 iterations in 15147 ms: 66 it/sec

After:

    == Testing with 1 MB ==
    fun base64:encode/1: 1000 iterations in 8550 ms: 116 it/sec
    fun base64:decode/1: 1000 iterations in 10066 ms: 99 it/sec
@bjorng
bjorng force-pushed the bjorn/jit/bs_create_bin branch from e5bd2c1 to 6fc65ab Compare June 8, 2022 08:00
@bjorng
bjorng merged commit 3e73fea into erlang:master Jun 8, 2022
@bjorng
bjorng deleted the bjorn/jit/bs_create_bin branch June 8, 2022 08:08
bjorng added a commit to bjorng/otp that referenced this pull request Aug 7, 2025
In Erlang/OTP 26 (in erlang#6031), the JIT learned to optimize binary
construction such as:

    <<A:16/big, B:32/big, C:16/big>>

The optimization is done on the native-code level, but the idea behind
it can be illustrated in Erlang by rewriting the construction as
follows:

    Acc0 = A,
    Acc1 = (Acc0 bsl 32) bor B,
    Acc = (Acc1 bsl 16) bor C,
    <<Acc:64/big>>

When done in native code, the values of the segments is accumulated
into a CPU register, which is then written to memory. This is faster
than writing each segment to memory one at a time, especially if the
sizes are not byte-sized as in the following example:

    <<A:6, B:6, C:6, D:6>>

This commit introduces a similar optimization for little-endian
integer segments. Example:

    <<A:16/little, B:32/little, C:16/little>>

This expression can be rewritten as follows:

    Acc0 = C,
    Acc1 = (Acc0 bsl 32) bor B,
    Acc = (Acc1 bsl 16) bor A,
    <<Acc:64/little>>

Note that this rewriting is only safe if all segment sizes are
byte-sized.
bjorng added a commit to bjorng/otp that referenced this pull request Aug 7, 2025
In Erlang/OTP 26 (in erlang#6031), the JIT learned to optimize binary
construction such as:

    <<A:16/big, B:32/big, C:16/big>>

The optimization is done on the native-code level, but the idea behind
it can be illustrated in Erlang by rewriting the construction as
follows:

    Acc0 = A,
    Acc1 = (Acc0 bsl 32) bor B,
    Acc = (Acc1 bsl 16) bor C,
    <<Acc:64/big>>

When done in native code, the values of the segments is accumulated
into a CPU register, which is then written to memory. This is faster
than writing each segment to memory one at a time, especially if the
sizes are not byte-sized as in the following example:

    <<A:6, B:6, C:6, D:6>>

This commit introduces a similar optimization for little-endian
integer segments. Example:

    <<A:16/little, B:32/little, C:16/little>>

This expression can be rewritten as follows:

    Acc0 = C,
    Acc1 = (Acc0 bsl 32) bor B,
    Acc = (Acc1 bsl 16) bor A,
    <<Acc:64/little>>

Note that this rewriting is only safe if all segments except the last
one are byte-sized.
bjorng added a commit to bjorng/otp that referenced this pull request Aug 8, 2025
In Erlang/OTP 26 (in erlang#6031), the JIT learned to optimize binary
construction such as:

    <<A:16/big, B:32/big, C:16/big>>

The optimization is done on the native-code level, but the idea behind
it can be illustrated in Erlang by rewriting the construction as
follows:

    Acc0 = A,
    Acc1 = (Acc0 bsl 32) bor B,
    Acc = (Acc1 bsl 16) bor C,
    <<Acc:64/big>>

When done in native code, the values of the segments is accumulated
into a CPU register, which is then written to memory. This is faster
than writing each segment to memory one at a time, especially if the
sizes are not byte-sized as in the following example:

    <<A:6, B:6, C:6, D:6>>

This commit introduces a similar optimization for little-endian
integer segments. Example:

    <<A:16/little, B:32/little, C:16/little>>

This expression can be rewritten as follows:

    Acc0 = C,
    Acc1 = (Acc0 bsl 32) bor B,
    Acc = (Acc1 bsl 16) bor A,
    <<Acc:64/little>>

Note that this rewriting is only safe if all segments except the last
one are byte-sized.
bjorng added a commit to bjorng/otp that referenced this pull request Aug 21, 2025
In Erlang/OTP 26 (in erlang#6031), the JIT learned to optimize binary
construction such as:

    <<A:16/big, B:32/big, C:16/big>>

The optimization is done on the native-code level, but the idea behind
it can be illustrated in Erlang by rewriting the construction as
follows:

    Acc0 = A,
    Acc1 = (Acc0 bsl 32) bor B,
    Acc = (Acc1 bsl 16) bor C,
    <<Acc:64/big>>

When done in native code, the values of the segments is accumulated
into a CPU register, which is then written to memory. This is faster
than writing each segment to memory one at a time, especially if the
sizes are not byte-sized as in the following example:

    <<A:6, B:6, C:6, D:6>>

This commit introduces a similar optimization for little-endian
integer segments. Example:

    <<A:16/little, B:32/little, C:16/little>>

This expression can be rewritten as follows:

    Acc0 = C,
    Acc1 = (Acc0 bsl 32) bor B,
    Acc = (Acc1 bsl 16) bor A,
    <<Acc:64/little>>

Note that this rewriting is only safe if all segments except the last
one are byte-sized.
bjorng added a commit to bjorng/otp that referenced this pull request Oct 31, 2025
In Erlang/OTP 26 (in erlang#6031), the JIT learned to optimize binary
construction such as:

    <<A:16/big, B:32/big, C:16/big>>

The optimization is done on the native-code level, but the idea behind
it can be illustrated in Erlang by rewriting the construction as
follows:

    Acc0 = A,
    Acc1 = (Acc0 bsl 32) bor B,
    Acc = (Acc1 bsl 16) bor C,
    <<Acc:64/big>>

When done in native code, the values of the segments is accumulated
into a CPU register, which is then written to memory. This is faster
than writing each segment to memory one at a time, especially if the
sizes are not byte-sized as in the following example:

    <<A:6, B:6, C:6, D:6>>

This commit introduces a similar optimization for little-endian
integer segments. Example:

    <<A:16/little, B:32/little, C:16/little>>

This expression can be rewritten as follows:

    Acc0 = C,
    Acc1 = (Acc0 bsl 32) bor B,
    Acc = (Acc1 bsl 16) bor A,
    <<Acc:64/little>>

Note that this rewriting is only safe if all segments except the last
one are byte-sized.
bjorng added a commit to bjorng/otp that referenced this pull request Oct 31, 2025
In Erlang/OTP 26 (in erlang#6031), the JIT learned to optimize binary
construction such as:

    <<A:16/big, B:32/big, C:16/big>>

The optimization is done on the native-code level, but the idea behind
it can be illustrated in Erlang by rewriting the construction as
follows:

    Acc0 = A,
    Acc1 = (Acc0 bsl 32) bor B,
    Acc = (Acc1 bsl 16) bor C,
    <<Acc:64/big>>

When done in native code, the values of the segments is accumulated
into a CPU register, which is then written to memory. This is faster
than writing each segment to memory one at a time, especially if the
sizes are not byte-sized as in the following example:

    <<A:6, B:6, C:6, D:6>>

This commit introduces a similar optimization for little-endian
integer segments. Example:

    <<A:16/little, B:32/little, C:16/little>>

This expression can be rewritten as follows:

    Acc0 = C,
    Acc1 = (Acc0 bsl 32) bor B,
    Acc = (Acc1 bsl 16) bor A,
    <<Acc:64/little>>

Note that this rewriting is only safe if all segments except the last
one are byte-sized.

Co-authored-by: John Högberg <john@erlang.org>
bjorng added a commit to bjorng/otp that referenced this pull request Nov 3, 2025
In Erlang/OTP 26 (in erlang#6031), the JIT learned to optimize binary
construction such as:

    <<A:16/big, B:32/big, C:16/big>>

The optimization is done on the native-code level, but the idea behind
it can be illustrated in Erlang by rewriting the construction as
follows:

    Acc0 = A,
    Acc1 = (Acc0 bsl 32) bor B,
    Acc = (Acc1 bsl 16) bor C,
    <<Acc:64/big>>

When done in native code, the values of the segments is accumulated
into a CPU register, which is then written to memory. This is faster
than writing each segment to memory one at a time, especially if the
sizes are not byte-sized as in the following example:

    <<A:6, B:6, C:6, D:6>>

This commit introduces a similar optimization for little-endian
integer segments. Example:

    <<A:16/little, B:32/little, C:16/little>>

This expression can be rewritten as follows:

    Acc0 = C,
    Acc1 = (Acc0 bsl 32) bor B,
    Acc = (Acc1 bsl 16) bor A,
    <<Acc:64/little>>

Note that this rewriting is only safe if all segments except the last
one are byte-sized.

Co-authored-by: John Högberg <john@erlang.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement team:VM Assigned to OTP team VM testing currently being tested, tag is used by OTP internal CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants