Skip to content

DArray: Add accumulate, cumsum and cumprod - #775

Merged
jpsamaroo merged 2 commits into
JuliaParallel:masterfrom
aalquwayfili:darray-accumulate
Oct 13, 2026
Merged

jpsamaroo merged 2 commits into
JuliaParallel:masterfrom
aalquwayfili:darray-accumulate

Conversation

@aalquwayfili

@aalquwayfili aalquwayfili commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #770.

accumulate had no DArray method, so Base walked it element by element. This adds one, and cumsum/cumprod use it.

Each partition is scanned on its own, then the last slice of the partitions before it is carried in along dims. The carries are one slice each, so only they run in order and the scans stay parallel. Like reduce this needs an associative op, so accumulate(-, DA) won't match Base.

One catch: only the first partition gets init, so with Int8 data and init=0 the later partitions overflow if you do nothing about it:

v = fill(Int8(100), 10)
collect(accumulate(+, distribute(v, Blocks(3)); init=0))
# without widening: [100, 200, 300, 400, 244, 344, 444, 288, 388, 488]

So when init widens the eltype, the partitions get widened before the scan. There's a test for that now.

array/mapreduce passes on 1.10, 1.11 and 1.12 (484 tests), and the gpu suite on a Quadro RTX 8000 (248). With the change reverted the new tests fail on both.

Rough timing on 8 threads (the machine was busy): cumsum(DA; dims=1) on 8192x8192 takes about 61 ms, vs 305 ms for Base on the same Array.

cumsum!/accumulate! into an existing DArray still fail, I left those for later. Also added the three to the supported list in the docs.

There was no `accumulate` method for a `DArray`, so `Base`'s generic one
scanned it element by element. With `dims` that throws a
`ConcurrencyViolationError`, and on a vector it returns a `DVector{Any}`.

Scan each partition on its own, then combine the running last slice of
the partitions before it along `dims` into it. Only the carries run in
sequence, and each one is a single slice. Like `reduce`, this assumes
`op` is associative. Only the first partition starts from `init`, so when
`init` widens the eltype the partitions are widened before the scan.
OpenCL.jl has no `accumulate` for `CLArray`, so `Base` falls back to scalar indexing. Metal has no Float64, so the `init=0.0` test can't run there.
@aalquwayfili

Copy link
Copy Markdown
Contributor Author

Pushed a fix for the OpenCL and Metal jobs.

OpenCL.jl has no accumulate for CLArray, so even a plain accumulate(+, A::CLArray) hits scalar indexing, and DArray accumulate can't work there yet. I marked it @test_broken so it shows up if that changes. Metal has no Float64, so I skipped the init=0.0 test there.

The other red jobs look unrelated, most of them fail on master too.

@jpsamaroo
jpsamaroo merged commit 9e78226 into JuliaParallel:master Oct 13, 2026
44 of 58 checks passed
@jpsamaroo

Copy link
Copy Markdown
Member

Thanks so much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cumsum(DA; dims) throws a ConcurrencyViolationError

2 participants