JIT: Optimize creation of binaries - #6031
Conversation
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.
CT Test Results 3 files 125 suites 37m 44s ⏱️ 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 |
8bf949d to
442cbff
Compare
845d29a to
69edb96
Compare
06f4a21 to
afd33b8
Compare
| mov_imm(ARG3, seg.effectiveSize); | ||
| } else if (seg.unit == 0) { | ||
| /* Silly but legal. */ | ||
| mov_imm(ARG3, 0); |
There was a problem hiding this comment.
According to gcov, this line is never run. Is that because the compiler will never emit such code?
There was a problem hiding this comment.
Contrary to the comment, a unit size of zero is not legal (at least for integer segments). I have removed the code.
|
|
||
| Uint error_info; | ||
| Sint effectiveSize; | ||
| enum class action { NONE, ACCUMULATE_FIRST, ACCUMULATE, STORE } action; |
There was a problem hiding this comment.
Would you mind adding a small description what the different actions do and when they trigger?
There was a problem hiding this comment.
I have renamed NONE to DIRECT and added more comments.
199ada3 to
e5bd2c1
Compare
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
e5bd2c1 to
6fc65ab
Compare
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.
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.
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.
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.
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.
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>
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>
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:
With this pull request:
On my M1 MacBook Pro from 2020 the result before was:
After: