diff --git a/src/bound_law.jl b/src/bound_law.jl index 792c1d9..a38d273 100644 --- a/src/bound_law.jl +++ b/src/bound_law.jl @@ -75,23 +75,6 @@ function MutableRelationStorage(storage; generation, status = nothing, validated_generations, Int(slot)) end -@kernel function _initialize_allocated_storage_kernel!(destination, value) - index = @index(Global, Linear) - index <= length(destination) && (@inbounds destination[index] = value) -end - -function _allocate_array(backend, ::Type{T}, shape::Tuple) where {T} - return KernelAbstractions.allocate(backend, T, shape) -end - -function _initialize_allocated_storage!(backend, destination, value) - isempty(destination) && return destination - kernel = _initialize_allocated_storage_kernel!(backend) - kernel(destination, value; ndrange = length(destination)) - KernelAbstractions.synchronize(backend) - return destination -end - function _copy_allocated_array(backend, source::AbstractArray) destination = _allocate_array(backend, eltype(source), size(source)) copyto!(destination, source) @@ -302,19 +285,6 @@ function _collect_allocation_schema(law::LocalLaw, collection::Collection) return first(schemas) end -function _filled_int32_storage(backend, length::Int, value::Int32) - storage = _allocate_array(backend, Int32, (length,)) - return _initialize_allocated_storage!(backend, storage, value) -end - -function _filled_uint64_storage(backend, length::Int, value::UInt64) - storage = _allocate_array(backend, UInt64, (length,)) - return _initialize_allocated_storage!(backend, storage, value) -end - -_zeroed_int32_storage(backend, length::Int) = - _filled_int32_storage(backend, length, Int32(0)) - function _collection_allocation( law::LocalLaw, collection::Collection, request::Allocate, backend, ) @@ -325,17 +295,14 @@ function _collection_allocation( )) schema = _collect_allocation_schema(law, collection) capacity = Int(collection.capacity) - records = _allocate_compacted_records(backend, eltype(collection), capacity) - count = _zeroed_int32_storage(backend, 1) - segment_starts = schema.grouped ? ( - _filled_int32_storage(backend, schema.groups + 1, Int32(1)) - ) : nothing - source_item = _zeroed_int32_storage(backend, capacity) - source_lane = _zeroed_int32_storage(backend, capacity) - source_position = schema.persistent_source_positions ? - _zeroed_int32_storage(backend, schema.source_position_count) : nothing - return CompactedStorage(_CONSTRUCTION_TOKEN, records, count, - segment_starts, source_item, source_lane, source_position) + return CompactedStorage( + backend, + eltype(collection), + capacity; + group_count = schema.grouped ? schema.groups : nothing, + source_items = schema.source_position_count, + source_position = schema.persistent_source_positions, + ) end function _append_fold_state_requirements!(fields, law::OrderedFold) diff --git a/src/compacted.jl b/src/compacted.jl index fde5c19..a768f9b 100644 --- a/src/compacted.jl +++ b/src/compacted.jl @@ -19,6 +19,36 @@ struct _PersistentSourcePosition end const _COMPACTED_MAX_ORDINAL = Int32(typemax(Int32) - 1) +@kernel function _initialize_allocated_storage_kernel!(destination, value) + index = @index(Global, Linear) + index <= length(destination) && (@inbounds destination[index] = value) +end + +function _allocate_array(backend, ::Type{T}, shape::Tuple) where {T} + return KernelAbstractions.allocate(backend, T, shape) +end + +function _initialize_allocated_storage!(backend, destination, value) + isempty(destination) && return destination + kernel = _initialize_allocated_storage_kernel!(backend) + kernel(destination, value; ndrange = length(destination)) + KernelAbstractions.synchronize(backend) + return destination +end + +function _filled_int32_storage(backend, length::Int, value::Int32) + storage = _allocate_array(backend, Int32, (length,)) + return _initialize_allocated_storage!(backend, storage, value) +end + +function _filled_uint64_storage(backend, length::Int, value::UInt64) + storage = _allocate_array(backend, UInt64, (length,)) + return _initialize_allocated_storage!(backend, storage, value) +end + +_zeroed_int32_storage(backend, length::Int) = + _filled_int32_storage(backend, length, Int32(0)) + """ BoundedGroupView{K,T} @@ -108,6 +138,12 @@ either `nothing` or a device `Int32[G+1]` directory, and `source_item` plus `source_lane` retain provenance. `source_position` is present only when a typed downstream request demands that projection. This value is not an `AbstractArray`; its inactive record tail has no value semantics. + +`CompactedStorage(backend, T, capacity; group_count=nothing, +source_items=capacity, source_position=false)` constructs a logically empty +store. The count and provenance arrays start at zero and a grouped directory +starts at one. Construction is a cold, synchronized storage operation; no +initialization work survives into planning or execution. """ struct CompactedStorage{R, C, S, I, L, P} records::R @@ -218,13 +254,13 @@ function CompactedStorage( group_count === nothing || (group_count isa Integer && !(group_count isa Bool) && group_count >= 0) || throw(ArgumentError( "group_count must be a nonnegative integer or nothing")) - count = KernelAbstractions.allocate(backend, Int32, (1,)) + count = _zeroed_int32_storage(backend, 1) segments = group_count === nothing ? nothing : - KernelAbstractions.allocate(backend, Int32, (Int(group_count) + 1,)) - source_item = KernelAbstractions.allocate(backend, Int32, (Int(capacity),)) - source_lane = KernelAbstractions.allocate(backend, Int32, (Int(capacity),)) + _filled_int32_storage(backend, Int(group_count) + 1, Int32(1)) + source_item = _zeroed_int32_storage(backend, Int(capacity)) + source_lane = _zeroed_int32_storage(backend, Int(capacity)) projection = source_position ? - KernelAbstractions.allocate(backend, Int32, (Int(source_items),)) : nothing + _zeroed_int32_storage(backend, Int(source_items)) : nothing return CompactedStorage(_CONSTRUCTION_TOKEN, _allocate_compacted_records(backend, T, Int(capacity)), count, segments, source_item, source_lane, projection) diff --git a/test/metal/localmath_authoring.jl b/test/metal/localmath_authoring.jl index b82eb3d..0712775 100644 --- a/test/metal/localmath_authoring.jl +++ b/test/metal/localmath_authoring.jl @@ -157,6 +157,24 @@ struct LocalMathMetalNode end @test Array(LocalMath.storage(rejected_fold, fold_output)) == Float32[-1, -1] + fresh = LocalMath.CompactedStorage( + backend, Int32, 3; + group_count = 2, source_items = 6, source_position = true) + @test Array(fresh.count) == Int32[0] + @test Array(fresh.segment_starts) == Int32[1, 1, 1] + @test Array(fresh.source_item) == zeros(Int32, 3) + @test Array(fresh.source_lane) == zeros(Int32, 3) + @test Array(fresh.source_position) == zeros(Int32, 6) + empty_fresh = LocalMath.CompactedStorage( + backend, Int32, 0; + group_count = 0, source_items = 0, source_position = true) + @test Array(empty_fresh.count) == Int32[0] + @test Array(empty_fresh.segment_starts) == Int32[1] + @test isempty(empty_fresh.records) + @test isempty(empty_fresh.source_item) + @test isempty(empty_fresh.source_lane) + @test isempty(empty_fresh.source_position) + records = LocalMath.Collection(Int32, 3) collected = LocalMath.@localmath item ∈ source begin records[item] = bounded_collect(Int32(item); maximum = 1, diff --git a/test/test_stage_collection_binding.jl b/test/test_stage_collection_binding.jl index 6f50743..fbd042b 100644 --- a/test/test_stage_collection_binding.jl +++ b/test/test_stage_collection_binding.jl @@ -38,11 +38,23 @@ end @testset "Collection and OrderedFold structural Stage boundary" begin backend = KernelAbstractions.CPU() allocated = LMCB.CompactedStorage( - backend, Int32, 4; group_count = 2, source_position = true) + backend, Int32, 4; + group_count = 2, source_items = 6, source_position = true) @test length(allocated.records) == 4 - @test length(allocated.count) == 1 - @test length(allocated.segment_starts) == 3 - @test length(allocated.source_position) == 4 + @test allocated.count == Int32[0] + @test allocated.segment_starts == Int32[1, 1, 1] + @test allocated.source_item == zeros(Int32, 4) + @test allocated.source_lane == zeros(Int32, 4) + @test allocated.source_position == zeros(Int32, 6) + empty_allocated = LMCB.CompactedStorage( + backend, Int32, 0; + group_count = 0, source_items = 0, source_position = true) + @test isempty(empty_allocated.records) + @test empty_allocated.count == Int32[0] + @test empty_allocated.segment_starts == Int32[1] + @test isempty(empty_allocated.source_item) + @test isempty(empty_allocated.source_lane) + @test isempty(empty_allocated.source_position) nodes = LMCB.Space(SCBNode, 3) collection = LMCB.Collection(Int32, 4) diff --git a/test/test_storage_authoring.jl b/test/test_storage_authoring.jl index df1a9de..f03fc50 100644 --- a/test/test_storage_authoring.jl +++ b/test/test_storage_authoring.jl @@ -287,7 +287,19 @@ end @test size(collection_storage.count) == (1,) @test size(collection_storage.segment_starts) == (3,) @test collection_storage.segment_starts == ones(Int32, 3) + @test collection_storage.count == Int32[0] + @test collection_storage.source_item == zeros(Int32, 3) + @test collection_storage.source_lane == zeros(Int32, 3) @test collection_storage.source_position === nothing + direct_collection_storage = LMA.CompactedStorage( + backend, Int32, 3; group_count = 2) + @test direct_collection_storage.count == collection_storage.count + @test direct_collection_storage.segment_starts == + collection_storage.segment_starts + @test direct_collection_storage.source_item == + collection_storage.source_item + @test direct_collection_storage.source_lane == + collection_storage.source_lane prepared_collection = LMA.prepare( LMA.plan(collection_bound; backend)) wait(LMA.execute!(prepared_collection))