Repository navigation
Carry the loop's float remainder as bits, so that it needs no stack slot - #55
Merged
Merged
Conversation
The float remainder that 81ddf67 added kept its result in a float across the branch for a zero remainder, and the register allocator gave it a stack slot. That grew fast's frame by 8 bytes, which moved the dispatch head's store of pc to 0x80(SP), past the reach of a one-byte displacement: the store grew by 3 bytes, and the indirect jump that follows now crossed a 32-byte boundary, which the assembler pads with a NOP. Every instruction the loop ran paid for it; fib ran 2.7% more instructions, nbody 1.8%. Built as bits in an integer register, the result needs no slot: the frame and the head are as before 81ddf67, and instruction counts are back to within 0.7% of it. The gate's baseline returns to its earlier trade: GETFIELD 7, GETTABLE 2.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the 1–4% slowdown on loop-bound scripts that #53 (81ddf67) introduced. #54 found it while rerunning the benchmarks.
Cause. The inline float remainder kept its result in a float register across the branch for a zero remainder (
r = math.Float64frombits(x.n & (1 << 63))). The register allocator spilled that float to a stack slot, which grewfast's frame from 0x30 to 0x38 bytes. That moved the slot where the dispatch head storespcfrom 0x78 to 0x80, which is past the reach of a one-byte displacement. The store therefore grew from 5 to 8 bytes, and the indirect jump right after it now crosses a 32-byte boundary. The Go assembler pads such jumps with a NOP (the mitigation for Intel's JCC erratum), so every bytecode the loop dispatches runs one more instruction:Fix. Build the result's bits in an integer register and convert once, in
setFloat. The semantics are unchanged: the zero case still takes bit 63 of the dividend. The frame and the dispatch head are back to what they were before #53, and the float remainder stays inline.Instructions per run, fast loop:
In time (minimum of 5 interleaved runs), against main, on the fast loop:
The asmgate baseline goes back to its pre-#53 trade: GETFIELD 7 stack accesses, GETTABLE 2 (instead of 6 and 3). On fieldread, which is mostly GETFIELD, that trade runs fewer instructions than main.