Repository navigation
Conversation
|
This is great, one of the primary reasons I added an explicit A test for such unmaterialised types - MappedArrays or simple ranges - would be very useful; and perhaps including such tests for the other functions, thanks! |
|
Unit tests added for range and mappedvector. I tried to also get this working for mapreduce_nd. There are a variety of InvalidIRError, for example "unsupported dynamic function invocation (call to convert)" and "unsupported dynamic function invocation (call to print_to_string". Test case: |
Backend resolution asked every array that is not a range, CartesianIndices or LinearIndices (or a Base view, reshape or permutation of one) for its backend with KernelAbstractions' `get_backend`, which recurses through `parent` and throws when it reaches a range or a tuple of parents. So `AK.sum(mappedarray(abs2, 1:n))`, and every other operation on a MappedArrays.jl array over ranges or Cartesian indices, threw "Implement get_backend" without an explicit `backend`, and `mappedarray(+, A, B)` threw a MethodError. With `backend` given, reductions, predicates, findall and searchsorted already worked on such arrays, since an `isbits` mapped array is passed to kernels as it is. But the operations that allocate a result (map, accumulate, reverse, sort, sortperm) used `similar(v)`, which for a wrapper without its own `similar` is Base's host `Array`, so their kernels got a host array. Copies of such wrappers (`sort`, the keys of the merge sort permutation, the host finish of small reductions) went through `copy`, `copyto!` or `Array`, whose fallbacks scalar-index device arrays. Now a wrapper votes like the arrays it wraps: `get_backend` is still asked where it can answer or where the wrapper defines its own method, and a wrapper of only backend-free arrays does not vote, as Base's wrappers of ranges already did not. A wrapper that gets Base's `similar` fallback, or wraps no memory, has its results allocated on `backend`. On kernel backends, wrappers that neither GPUArrays (its wrappers of device arrays) nor Base (wrappers of host memory) can copy are copied with a kernel; lazy arrays over ranges are not evaluated on the host either, since their function may read device arrays it captures. Everything else is allocated and copied as before. `reverse!` without `dims` indexed `dst` with the source's `eachindex`, which is Cartesian for a strided view or a lazy array over CartesianIndices, and threw a MethodError; it now pairs elements in linear order. A lazy array of device arrays also needs an Adapt.jl rule for its type to reach kernels. MappedArrays.jl has none, so `mappedarray(f, ::CuArray)` still only works on the host; that rule belongs in MappedArrays.jl. The tests use a lazy wrapper with such a rule for the device path. Supersedes #23, which allocated the 1D reduction's scratch on the given backend instead of with `similar(src)`, and whose range and mapped-array sums are among the tests. Co-authored-by: Dan Stahlke <dan@stahlke.org>
Backend resolution asked every array that is not a range, CartesianIndices or LinearIndices (or a Base view, reshape or permutation of one) for its backend with KernelAbstractions' `get_backend`, which recurses through `parent` and throws when it reaches a range or a tuple of parents. So `AK.sum(mappedarray(abs2, 1:n))`, and every other operation on a MappedArrays.jl array over ranges or Cartesian indices, threw "Implement get_backend" without an explicit `backend`, and `mappedarray(+, A, B)` threw a MethodError. With `backend` given, reductions, predicates, findall and searchsorted already worked on such arrays, since an `isbits` mapped array is passed to kernels as it is. But the operations that allocate a result (map, accumulate, reverse, sort, sortperm) used `similar(v)`, which for a wrapper without its own `similar` is Base's host `Array`, so their kernels got a host array. Copies of such wrappers (`sort`, the keys of the merge sort permutation, the host finish of small reductions) went through `copy`, `copyto!` or `Array`, whose fallbacks scalar-index device arrays. Now a wrapper votes like the arrays it wraps: `get_backend` is still asked where it can answer or where the wrapper defines its own method, and a wrapper of only backend-free arrays does not vote, as Base's wrappers of ranges already did not. A wrapper that gets Base's `similar` fallback, or wraps no memory, has its results allocated on `backend`. On kernel backends, wrappers that neither GPUArrays (its wrappers of device arrays) nor Base (wrappers of host memory) can copy are copied with a kernel; lazy arrays over ranges are not evaluated on the host either, since their function may read device arrays it captures. Everything else is allocated and copied as before. `reverse!` without `dims` indexed `dst` with the source's `eachindex`, which is Cartesian for a strided view or a lazy array over CartesianIndices, and threw a MethodError; it now pairs elements in linear order. A lazy array of device arrays also needs an Adapt.jl rule for its type to reach kernels. MappedArrays.jl has none, so `mappedarray(f, ::CuArray)` still only works on the host; that rule belongs in MappedArrays.jl. The tests use a lazy wrapper with such a rule for the device path. Supersedes #23, which allocated the 1D reduction's scratch on the given backend instead of with `similar(src)`, and whose range and mapped-array sums are among the tests. Co-authored-by: Dan Stahlke <dan@stahlke.org>
Backend resolution asked every array that is not a range, CartesianIndices or LinearIndices (or a Base view, reshape or permutation of one) for its backend with KernelAbstractions' `get_backend`, which recurses through `parent` and throws when it reaches a range or a tuple of parents. So `AK.sum(mappedarray(abs2, 1:n))`, and every other operation on a MappedArrays.jl array over ranges or Cartesian indices, threw "Implement get_backend" without an explicit `backend`, and `mappedarray(+, A, B)` threw a MethodError. With `backend` given, reductions, predicates, findall and searchsorted already worked on such arrays, since an `isbits` mapped array is passed to kernels as it is. But the operations that allocate a result (map, accumulate, reverse, sort, sortperm) used `similar(v)`, which for a wrapper without its own `similar` is Base's host `Array`, so their kernels got a host array. Copies of such wrappers (`sort`, the keys of the merge sort permutation, the host finish of small reductions) went through `copy`, `copyto!` or `Array`, whose fallbacks scalar-index device arrays. Now a wrapper whose parents are not a single array with memory votes like the arrays it wraps, so a wrapper of only ranges does not vote, as Base's wrappers of ranges already did not; other arrays are still asked with `get_backend`. A wrapper that gets Base's `similar` fallback, or wraps no memory, has its results allocated on `backend`, and is copied with a kernel there; other wrappers keep their own `copy`, so an OffsetArray's copy keeps its axes. Copies into existing arrays and to the host also use a kernel for wrappers that GPUArrays (its wrappers of device arrays) and Base (wrappers of host memory) do not copy, since Base's fallbacks index device memory element by element. Lazy arrays over ranges are not evaluated on the host either, since their function may read device arrays it captures. A lazy array of device arrays also needs an Adapt.jl rule for its type to reach kernels. MappedArrays.jl has none, so `mappedarray(f, ::CuArray)` still only works on the host; that rule belongs in MappedArrays.jl. The tests use a lazy wrapper with such a rule for the device path. Supersedes #23, which allocated the 1D reduction's scratch on the given backend instead of with `similar(src)`, and whose range and mapped-array sums are among the tests. Co-authored-by: Dan Stahlke <dan@stahlke.org>
This allows calling reduce on MappedArrays types. That's nice because you can avoid allocating a large array to store the input. For example,
I'm creating a bare-bones pull request to get feedback on whether this is desired. If so, I can add a unit test.