perf: optimize compute.Take for fewer memory allocations - #557
Merged
Conversation
zeroshade
requested changes
Oct 30, 2025
zeroshade
left a comment
Member
There was a problem hiding this comment.
Can we add a benchmark test to compute/vector_selection_test.go for future reference?
Contributor
Author
|
on this PR: |
zeroshade
approved these changes
Oct 31, 2025
This was referenced Nov 13, 2025
zeroshade
pushed a commit
that referenced
this pull request
Nov 14, 2025
### Rationale for this change This PR implements the same allocation improvements as #557 but for the Take list kernel. ### What changes are included in this PR? - Pre-allocate upfront with a better estimate of the necessary buffer size to eliminate repeated reallocations. - Use exponential growth for additional allocations for O(log n) total reallocations. ### Are these changes tested? New benchmarks added for take list kernel ### Are there any user-facing changes? No ### Performance Comparison on `main` ``` goos: darwin goarch: arm64 pkg: github.com/apache/arrow-go/v18/arrow/compute cpu: Apple M3 Max BenchmarkTakeList/SmallBatch_ShortLists-16 2320 555889 ns/op 1798923 rows/sec 3355390 B/op 396 allocs/op BenchmarkTakeList/MediumBatch_ShortLists-16 31 37600434 ns/op 265955 rows/sec 324238694 B/op 3214 allocs/op BenchmarkTakeList/LargeBatch_ShortLists-16 2 653682896 ns/op 76490 rows/sec 7877124660 B/op 15751 allocs/op BenchmarkTakeList/XLargeBatch_ShortLists-16 1 2019873959 ns/op 49508 rows/sec 31380868976 B/op 31489 allocs/op BenchmarkTakeList/SmallBatch_MediumLists-16 334 3435205 ns/op 291104 rows/sec 43059334 B/op 1086 allocs/op BenchmarkTakeList/MediumBatch_MediumLists-16 5 230308533 ns/op 43420 rows/sec 4041679715 B/op 10098 allocs/op BenchmarkTakeList/LargeBatch_MediumLists-16 1 4600489959 ns/op 10868 rows/sec 100213267960 B/op 50328 allocs/op BenchmarkTakeList/XLargeBatch_MediumLists-16 1 16462343792 ns/op 6074 rows/sec 400427784144 B/op 101032 allocs/op BenchmarkTakeList/LargeBatch_ShortLists_Large-16 1 1682293042 ns/op 29721 rows/sec 31379855376 B/op 31422 allocs/op BenchmarkTakeList/XLargeBatch_MediumLists_Large-16 1 33800353917 ns/op 2959 rows/sec 800433389776 B/op 102480 allocs/op BenchmarkTakeListPartitionPattern-16 1 3003105000 ns/op 16649 rows/sec 69581474288 B/op 46479 allocs/op PASS ok github.com/apache/arrow-go/v18/arrow/compute 71.015s ``` on this branch ``` goos: darwin goarch: arm64 pkg: github.com/apache/arrow-go/v18/arrow/compute cpu: Apple M3 Max BenchmarkTakeList/SmallBatch_ShortLists-16 25522 46138 ns/op 21674062 rows/sec 50668 B/op 83 allocs/op BenchmarkTakeList/MediumBatch_ShortLists-16 3792 316046 ns/op 31641022 rows/sec 457558 B/op 83 allocs/op BenchmarkTakeList/LargeBatch_ShortLists-16 804 1521240 ns/op 32867968 rows/sec 2232578 B/op 84 allocs/op BenchmarkTakeList/XLargeBatch_ShortLists-16 416 2832247 ns/op 35307705 rows/sec 4435314 B/op 84 allocs/op BenchmarkTakeList/SmallBatch_MediumLists-16 9444 125321 ns/op 7979591 rows/sec 173603 B/op 83 allocs/op BenchmarkTakeList/MediumBatch_MediumLists-16 1176 999217 ns/op 10007857 rows/sec 1653926 B/op 83 allocs/op BenchmarkTakeList/LargeBatch_MediumLists-16 232 4913249 ns/op 10176590 rows/sec 8229752 B/op 85 allocs/op BenchmarkTakeList/XLargeBatch_MediumLists-16 128 9309120 ns/op 10742230 rows/sec 16428821 B/op 85 allocs/op BenchmarkTakeList/LargeBatch_ShortLists_Large-16 739 1560044 ns/op 32050453 rows/sec 3428837 B/op 84 allocs/op BenchmarkTakeList/XLargeBatch_MediumLists_Large-16 122 9712600 ns/op 10295969 rows/sec 24834215 B/op 85 allocs/op BenchmarkTakeListPartitionPattern-16 96 11756706 ns/op 4252901 rows/sec 18421151 B/op 98 allocs/op PASS ok github.com/apache/arrow-go/v18/arrow/compute 17.869s ```
zeroshade
pushed a commit
that referenced
this pull request
Dec 1, 2025
### Rationale for this change Arrow Go is lacking a Take kernel for Map types which means from `iceberg-go` we cannot write to partitioned Iceberg tables containing columns with Map types. ### What changes are included in this PR? - Adds a new Take kernel for Map types - the same allocation behavior from #557 and #573 is used ### Are these changes tested? Yes. ### Are there any user-facing changes? * Can now use Take on Arrow schemas with Map columns. * Can now write to partitioned Iceberg tables using `arrow-go`
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.
Rationale for this change
When writing data to partitioned Iceberg tables using
iceberg-go's new partitioned write capability, I found that Arrow'scompute.Takeoperation was causing the writes to be significantly slower than unpartitioned writes.Causes:
VarBinaryImplfunction here inarrow-gowas pre-allocating the data buffer with onlymeanValueLen(size for just one string/buffer), not the total size needed for all strings. For, my use case (writing 100k rows at a time) this was causing 15x buffer reallocations as the buffer incrementally scales up. For my iceberg table with 20+ string/binary cols of various size this is significant overhead.VarBinaryImplallocates additional space for new items, it would only add the exact amount needed for the new value. This caused O(n) reallocations.What changes are included in this PR?
Pre-allocate upfront with a better estimate of the necessary buffer size to eliminate repeated reallocations.
I somewhat arbitrarily chose a cap of 16MB as a guess at what would be effective at reducing the number of allocations but also trying to be cognizant of the library being used in many bespoke scenarios and not wanting to make massive memory spikes. For my use case, I never hit this 16MB threshold and it could be smaller.
I am curious for your input on whether there should be a cap at all or what a reasonable cap would be.
Use exponential growth for additional allocations for O(log n) total reallocations.
Are these changes tested?
No unit tests.
However, with a dedicated reproducing script that mimics my use case with different write sizes (on a macbook pro m3 max + 64GB ram) average of 3
table.Append()calls:Are there any user-facing changes?
No; just more performant