-
Notifications
You must be signed in to change notification settings - Fork 391
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inconsistent performance with order of operations with constant #626
Comments
Yeah this is a current limitation of the bytecode encoding. For optimal performance right now you'd want something like We were planning on lifting that for non-commutative operators ( benchmarks:
lua-apps:
|
A quick test indicates that this would increase the interpreter code (binary) size by about 1 KB, so this will need to wait until we stop supporting older bytecode versions so that we don't increase the L1 cache footprint unnecessarily. |
Right now, we can compile R\*K for all arithmetic instructions, but K\*R gets compiled into two instructions (LOADN/LOADK + arithmetic opcode). This is problematic since it leads to reduced performance for some code. However, we'd like to avoid adding reverse variants of ADDK et al for all opcodes to avoid the increase in I$ footprint for interpreter. Looking at the arithmetic instructions, % and // don't have interesting use cases for K\*V; ^ is sometimes used with constant on the left hand side but this would need to call pow() by necessity in all cases so it would be slow regardless of the dispatch overhead. This leaves the four basic arithmetic operations. For + and \*, we can implement a compiler-side optimization in the future that transforms K\*R to R\*K automatically. This could either be done unconditionally at -O2, or conditionally based on the type of the value (driven by type annotations / inference) -- this technically changes behavior in presence of metamethods, although it might be sensible to just always do this because non-commutative +/* are evil. However, for - and / it is impossible for the compiler to optimize this in the future, so we need dedicated opcodes. This only increases the interpreter size by ~300 bytes (~1.5%) on X64. This makes spectral-norm and math-partial-sums 6% faster; maybe more importantly, voxelgen gets 1.5% faster (so this change does have real-world impact). To avoid the proliferation of bytecode versions this change piggybacks on the bytecode version bump that was just made in 604 for vector constants; we would still be able to enable these independently but we'll consider v5 complete when both are enabled. Related: #626 --------- Co-authored-by: vegorov-rbx <[email protected]>
When type information is specified, we can compile k*n and k+n into MULK/ADDK forms that are faster to execute, as long as we think n is a number. Since we generally restrict type aware optimizations to O2, this does that as well. This makes trig benchmark ~4% faster on Apple M2 in VM, and also a tiny improvement on scimark (~0.1%) can be observed. The optimization only affects interpreted execution, as NCG already can synthesize optimal code here. If the type information is not truthful (e.g. user annotates type as a number and it's not), the worst case scenario is flipped arguments to metamethods like __add/__mul for constant left hand side. Fixes #626 (the fix requires type information or NCG but I doubt any further work on this is warranted) --------- Co-authored-by: vegorov-rbx <[email protected]>
Usually, one should try to have constants of multiplicaiton at the front so that the constant value can be pre-computed for performance. For example, doing
3^0.5/2*x
instead ofx*3^0.5/2
. However, If one does this in Luau, there is a considerable performance penalty, and the latter ends up being faster.To reproduce this bug, run this code:
Output should look something like this:
In this case, multiplication with a constant up front is specifically slower than all other options.
The text was updated successfully, but these errors were encountered: