crypto/blake2b: bound input of assembly call - #35550
Open
AskAlexSharov wants to merge 2 commits into
Open
Conversation
The rounds argument of the BLAKE2b F precompile comes straight from calldata as a uint32 and is priced at one gas per round, so a single transaction can ask for tens of millions of rounds. fAVX2, fAVX and fSSE4 run that whole loop inside NOSPLIT assembly, which the runtime cannot preempt, so one call holds every P in stop-the-world for its duration: 402 ms for a full block of gas. F now hands anything over 4090 rounds to fLong, which splits it into chunks. The existing assembly cannot be split -- it derives the working vector v from h, runs every round and folds v back into h inside one call -- so a new entry point fAVX2Rounds takes v through memory instead, letting a long F resume between chunks. The chunk size is a multiple of 10 because the round function permutes the message with period 10 and the assembly unrolls exactly those ten permutations. Worst stop-the-world stopping pause drops from 402.653 ms to 0.057 ms at 45,000,000 rounds, and from 8.389 ms to 0.057 ms at 1,048,576. Throughput is unchanged at 7.59 ns/round.
The 10 was repeated as a literal in the table size and the round index, with the chunk-alignment rule stated only in prose. sigmaRounds carries it, the table is declared with it, and a compile-time check pins maxAsmRounds to a whole cycle.
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.
Problem
GC stop-the-world is slow if a large
roundsis passed to the BLAKE2bFprecompile.Root cause:
roundscomes straight fromcalldataas auint32and is priced at one gas per round, so a single transaction can ask for tens of millions of rounds.fAVX2,fAVXandfSSE4run that whole loop insideNOSPLITassembly:Max GC stop-the-world stopping pause (
/sched/pauses/stopping/gc:seconds), EPYC 4344P,GOMAXPROCS=2:Throughput is unchanged — ns/round, best of 5 runs, no collector running:
Fix
Fhands anything over 4090 rounds tofLong, which splits it into chunks.The existing assembly cannot be split: it derives the working vector
vfromh, runs every round, and foldsvback intoh, all inside one call. So a new entry pointfAVX2Roundsloadsvfrom memory and stores it back, which is what lets a longFresume between chunks. The chunk size is a multiple of 10 because the round function permutes the message with period 10 and the assembly unrolls exactly those ten permutations — a chunk ending mid-cycle would restart at the wrong one.fRoundsis//go:noinlineon purpose: its prologue carries the stack-growth check that is the chunk loop's only preemption point. Inlined, the loop would callNOSPLITassembly directly and be unpreemptible again.TestFChunkedMatchesGenericchecks the chunked assembly against the pure-Go reference across round counts, including the chunk boundary.References: