-
Notifications
You must be signed in to change notification settings - Fork 227
feat: add parallel prefix product for vector e4 #750
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ import ( | |
| "math/big" | ||
| "reflect" | ||
| "sort" | ||
| "runtime" | ||
| "bytes" | ||
|
|
||
| fr "{{ .FieldPackagePath }}" | ||
|
|
@@ -600,6 +601,49 @@ func TestVectorExp(t *testing.T) { | |
|
|
||
| } | ||
|
|
||
| // prefixProductGeneric computes the prefix product of the vector in place (single-threaded). | ||
| func prefixProductGeneric(vector Vector) { | ||
| if len(vector) == 0 { | ||
| return | ||
| } | ||
| for i := 1; i < len(vector); i++ { | ||
| vector[i].Mul(&vector[i-1], &vector[i]) | ||
| } | ||
| } | ||
|
|
||
| func randomVector(size int) Vector { | ||
| v := make(Vector, size) | ||
| for i := range v { | ||
| v[i].MustSetRandom() | ||
| } | ||
| return v | ||
| } | ||
|
|
||
| func TestPrefixProduct_EmptyVector(t *testing.T) { | ||
| assert := require.New(t) | ||
| v := make(Vector, 0) | ||
| expected := make(Vector, 0) | ||
| prefixProductGeneric(expected) | ||
| v.PrefixProduct() | ||
| assert.Equal(expected, v) | ||
| } | ||
|
|
||
| func TestPrefixProduct_VariousNbTasks(t *testing.T) { | ||
| assert := require.New(t) | ||
| sizes := []int{1, 2, 256, 1024} | ||
| nbTasksList := []int{1, 16, 32, runtime.NumCPU()} | ||
| for _, size := range sizes { | ||
| for _, nbTasks := range nbTasksList { | ||
| v := randomVector(size) | ||
| expected := make(Vector, size) | ||
| copy(expected, v) | ||
| prefixProductGeneric(expected) | ||
| v.PrefixProduct(nbTasks) | ||
| assert.Equal(expected, v, "size=%d nbTasks=%d", size, nbTasks) | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| func TestVectorEmptyOps(t *testing.T) { | ||
| assert := require.New(t) | ||
|
|
@@ -869,6 +913,28 @@ func BenchmarkVectorOps(b *testing.B) { | |
| } | ||
| } | ||
|
|
||
| func BenchmarkPrefixProduct(b *testing.B) { | ||
| const N = 1 << 19 | ||
| a1 := make(Vector, N) | ||
| for i := 0; i < N; i++ { | ||
| a1[i].MustSetRandom() | ||
| } | ||
|
|
||
| b.Run("generic", func(b *testing.B) { | ||
| b.ResetTimer() | ||
| for i := 0; i < b.N; i++ { | ||
| prefixProductGeneric(a1) | ||
| } | ||
| }) | ||
|
|
||
| b.Run("PrefixProduct", func(b *testing.B) { | ||
| b.ResetTimer() | ||
| for i := 0; i < b.N; i++ { | ||
| a1.PrefixProduct() | ||
| } | ||
| }) | ||
|
|
||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: In-place Modification Corrupts Benchmark DataThe Additional Locations (1) |
||
|
|
||
|
|
||
| func BenchmarkVectorSerialization(b *testing.B) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -7,6 +7,9 @@ import ( | |||||
| "bytes" | ||||||
| "slices" | ||||||
| "encoding/binary" | ||||||
| "runtime" | ||||||
| "github.com/consensys/gnark-crypto/internal/parallel" | ||||||
| "sync" | ||||||
|
|
||||||
| fr "{{ .FieldPackagePath }}" | ||||||
| {{- if .IsKoalaBear}} | ||||||
|
|
@@ -537,6 +540,77 @@ func (vector *Vector) ReadFrom(r io.Reader) (int64, error) { | |||||
| return n, <-chErr | ||||||
| } | ||||||
|
|
||||||
| // PrefixProduct computes the prefix product of the vector in place. | ||||||
| // i.e. vector[i] = vector[0] * vector[1] * ... * vector[i] | ||||||
| // If nbTasks > 1, it uses nbTasks goroutines to compute the prefix product in parallel. | ||||||
| // If nbTasks is not provided, it uses the number of CPU cores. | ||||||
| func (vector Vector) PrefixProduct(nbTasks ...int) { | ||||||
| N := len(vector) | ||||||
| if N < 2 { | ||||||
| return | ||||||
| } | ||||||
|
|
||||||
| if N < 512 { | ||||||
| vector.prefixProductGeneric() | ||||||
| return | ||||||
| } | ||||||
|
|
||||||
| // Use one worker per available CPU core. | ||||||
| numWorkers := runtime.GOMAXPROCS(0) | ||||||
| if len(nbTasks) == 1 && nbTasks[0] > 0 && nbTasks[0] < numWorkers { | ||||||
| numWorkers = nbTasks[0] | ||||||
| } | ||||||
|
|
||||||
| for N / numWorkers < 64 && numWorkers > 1 { | ||||||
| numWorkers >>= 1 | ||||||
| } | ||||||
| numWorkers = max(1, numWorkers) | ||||||
|
|
||||||
| // --- PASS 1: Calculate prefix product for each chunk independently --- | ||||||
|
||||||
| // --- PASS 1: Calculate prefix product for each chunk independently --- | |
| // --- PASS 1: Calculate prefix product for each chunk independently --- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The multiplication order is incorrect. This should be
vector[i].Mul(&vector[i], &vector[i-1])to match the prefix product definition where each element becomes the product of itself with all previous elements.