@@ -469,7 +469,10 @@ func (n *InternalNode) Delete(key []byte, resolver NodeResolverFn) error {
469469func (n * InternalNode ) Flush (flush NodeFlushFn ) {
470470 n .Commit ()
471471
472- if n .depth <= 0 {
472+ // If we're at the root internal node, we fire goroutines exploiting
473+ // available cores. Those goroutines will recursively take care of downstream
474+ // layers, so we avoid creating too many goroutines.
475+ if n .depth == 0 {
473476 batches := runtime .NumCPU ()
474477 batchSize := len (n .children ) / batches
475478 var wg sync.WaitGroup
@@ -585,8 +588,12 @@ func (n *InternalNode) Commitment() *Point {
585588}
586589
587590func (n * InternalNode ) Commit () * Point {
591+ // If we're at the first or second layer, we do the Commit() in parallel
592+ // leveraging available cores.
593+ // If we're in further layers, we do single-goroutine Commit() since the top
594+ // two layers already created enough goroutines doing downstream work.
588595 if n .depth <= 1 {
589- return n .commitRoot ()
596+ return n .commitParallel ()
590597 }
591598 if len (n .cow ) != 0 {
592599 polyp := frPool .Get ().(* []Fr )
@@ -627,7 +634,7 @@ func (n *InternalNode) Commit() *Point {
627634 return n .commitment
628635}
629636
630- func (n * InternalNode ) commitRoot () * Point {
637+ func (n * InternalNode ) commitParallel () * Point {
631638 if len (n .cow ) != 0 {
632639 polyp := frPool .Get ().(* []Fr )
633640 poly := * polyp
@@ -639,50 +646,74 @@ func (n *InternalNode) commitRoot() *Point {
639646 }()
640647 emptyChildren := 256
641648
649+ // The idea below is to distribute calling Commit() in all COW-ed children in multiple goroutines.
650+ // For example, if we have 2-cores, we calculate the first half of COW-ed new Commit() in a goroutine, and
651+ // the other half in another goroutine.
652+
653+ // In `points` we'll have:
654+ // - point[2*i]: the previous *Point value saved by COW.
655+ // - point[2*i+1]: we'll calculate the new Commit() of that leaf value.
656+ //
657+ // First, we create the arrays to store this.
642658 var i int
643- b := make ([]byte , len (n .cow ))
659+ pointsIndexes := make ([]byte , len (n .cow ))
644660 points := make ([]* Point , 2 * len (n .cow ))
645661 for idx := range n .cow {
646662 emptyChildren --
647- b [i ] = idx
663+ pointsIndexes [i ] = idx
648664 i ++
649665 }
650666
651667 var wg sync.WaitGroup
652- f := func (start , end int ) {
668+ // `calculateChildsCommsInRange` does the mentioned calculation in `points`.
669+ // It receives the range in the array where it should do the work. As mentioned earlier, each goroutine
670+ // is assigned a range to do work. The complete range work is distributed in multiple goroutines.
671+ calculateChildsCommsInRange := func (start , end int ) {
653672 defer wg .Done ()
654673 for i := start ; i < end ; i ++ {
655- points [2 * i ] = n.cow [b [i ]]
656- points [2 * i + 1 ] = n.children [b [i ]].Commit ()
674+ points [2 * i ] = n.cow [pointsIndexes [i ]]
675+ points [2 * i + 1 ] = n.children [pointsIndexes [i ]].Commit ()
657676 }
658677 }
678+ // Here we do the work distribution. We split the total range of work to do in `numBatches` batches and call
679+ // the above function which does the work.
659680 numBatches := runtime .NumCPU ()
660681 wg .Add (numBatches )
661682 for i := 0 ; i < numBatches ; i ++ {
683+ batchStart := i * (len (pointsIndexes ) / numBatches )
662684 if i < numBatches - 1 {
663- go f ( i * ( len ( b ) / numBatches ) , (i + 1 )* (len (b )/ numBatches ))
685+ go calculateChildsCommsInRange ( batchStart , (i + 1 )* (len (pointsIndexes )/ numBatches ))
664686 } else {
665- go f ( i * ( len ( b ) / numBatches ) , len (b ))
687+ go calculateChildsCommsInRange ( batchStart , len (pointsIndexes ))
666688 }
667689 }
668690
691+ // After calculating the new *Point (Commit() of touched children), we'll have to do the Point->Fr transformation.
669692 frs := make ([]* Fr , len (points ))
670693 for i := range frs {
671694 if i % 2 == 0 {
695+ // For even slots (old COW commitment), we create a new empty Fr to store the result.
672696 frs [i ] = & Fr {}
673697 } else {
674- frs [i ] = & poly [b [i / 2 ]]
698+ // For odd slots (new commitment), we can use `poly` as a temporal storage to avoid allocations.
699+ frs [i ] = & poly [pointsIndexes [i / 2 ]]
675700 }
676701 }
677702 wg .Wait ()
678703
704+ // Now that in `frs` we have where we want to store *all* the Point->Fr transformations, we do that in a single batch.
679705 toFrMultiple (frs , points )
706+
707+ // For each
680708 for i := 0 ; i < len (points )/ 2 ; i ++ {
681- poly [b [i ]].Sub (frs [2 * i + 1 ], frs [2 * i ])
709+ // Now we do [newCommitment] - [oldCommitment], so we know the Fr difference between old and new commitments.
710+ poly [pointsIndexes [i ]].Sub (frs [2 * i + 1 ], frs [2 * i ])
682711 }
683712
684713 n .cow = nil
685714
715+ // Now that in `poly` we have the Fr differences, we `CommitToPoly` and add to the current internal node
716+ // commitment, finishing the diff-updating.
686717 n .commitment .Add (n .commitment , cfg .CommitToPoly (poly , emptyChildren ))
687718 return n .commitment
688719 }
@@ -1114,24 +1145,15 @@ func (leaf *LeafNode) Commit() *Point {
11141145 // Initialize the commitment with the extension tree
11151146 // marker and the stem.
11161147 count := 0
1117- polyp , c1polyp := frPool .Get ().(* []Fr ), frPool .Get ().(* []Fr )
1118- // TODO(jsign): remove "poly" and reuse again c1poly
1119- poly , c1poly := * polyp , * c1polyp
1148+ c1polyp := frPool .Get ().(* []Fr )
1149+ c1poly := * c1polyp
11201150 defer func () {
11211151 for i := 0 ; i < 256 ; i ++ {
1122- poly [i ] = Fr {}
11231152 c1poly [i ] = Fr {}
11241153 }
1125- frPool .Put (polyp )
11261154 frPool .Put (c1polyp )
11271155 }()
1128- poly [0 ].SetUint64 (1 )
1129- StemFromBytes (& poly [1 ], leaf .stem )
11301156
1131- // TODO(jsign)
1132- if len (leaf .values ) != 256 {
1133- panic ("leaf doesn't have 256 values" )
1134- }
11351157 count = fillSuffixTreePoly (c1poly [:], leaf .values [:128 ])
11361158 leaf .c1 = cfg .CommitToPoly (c1poly [:], 256 - count )
11371159
@@ -1141,8 +1163,14 @@ func (leaf *LeafNode) Commit() *Point {
11411163 count = fillSuffixTreePoly (c1poly [:], leaf .values [128 :])
11421164 leaf .c2 = cfg .CommitToPoly (c1poly [:], 256 - count )
11431165
1144- toFrMultiple ([]* Fr {& poly [2 ], & poly [3 ]}, []* Point {leaf .c1 , leaf .c2 })
1145- leaf .commitment = cfg .CommitToPoly (poly [:], 252 )
1166+ for i := 0 ; i < 256 ; i ++ {
1167+ c1poly [i ] = Fr {}
1168+ }
1169+ c1poly [0 ].SetUint64 (1 )
1170+ StemFromBytes (& c1poly [1 ], leaf .stem )
1171+
1172+ toFrMultiple ([]* Fr {& c1poly [2 ], & c1poly [3 ]}, []* Point {leaf .c1 , leaf .c2 })
1173+ leaf .commitment = cfg .CommitToPoly (c1poly [:], 252 )
11461174
11471175 } else if len (leaf .cow ) != 0 {
11481176 // If we've already have a calculated commitment, and there're touched leaf values, we do a diff update.
0 commit comments