Skip to content

Commit 2d46926

Browse files
authored
Avoid allocation of temp array for dense data reads and writes (#161)
* introduce diskindex type * Add Aliasing types, breaks inference * revert alignment stuff * don't allocate when output array can be used * Add more getindex/setindex tests * more tests * more tests * More tests (again) * Remove unused methods * Remove assume_effects * Fix signature * fix dispatch for breaking out stepranges * bump julia compat to 1.9 * test on 1.9 * make 1.9 tests pass * Add type for ChunkStrategy
1 parent b49e9c8 commit 2d46926

File tree

6 files changed

+179
-78
lines changed

6 files changed

+179
-78
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ jobs:
1515
fail-fast: false
1616
matrix:
1717
version:
18+
- '1.9'
1819
- '1' # Leave this line unchanged. '1' will automatically expand to the latest stable 1.x release of Julia.
1920
- 'nightly'
2021
os:

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
1010
[compat]
1111
Aqua = "0.8"
1212
LRUCache = "1"
13-
julia = "1.6"
13+
julia = "1.9"
1414
OffsetArrays = "1"
1515
Statistics = "1.9"
1616
Test = "1.9"

src/batchgetindex.jl

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ function is_sparse_index(ids; density_threshold = 0.5)
8686
end
8787

8888
function process_index(i, cs, strategy::Union{ChunkRead,SubRanges})
89-
outsize, tempsize, outinds,tempinds,datainds,cs = process_index(i,cs, NoBatch(strategy))
90-
outsize, tempsize, ([outinds],), ([tempinds],), ([datainds],), cs
89+
ii,cs = process_index(i,cs, NoBatch(strategy))
90+
DiskIndex(ii.output_size, ii.temparray_size, ([ii.output_indices],), ([ii.temparray_indices],), ([ii.data_indices],)), cs
9191
end
9292

9393

@@ -111,7 +111,7 @@ function process_index(i::AbstractArray{<:Integer,N}, cs, ::ChunkRead) where N
111111
push!(tempinds, (tempind,))
112112
maxtempind = max(maxtempind,maximum(tempind))
113113
end
114-
size(i), ((maxtempind),), (outinds,), (tempinds,), (datainds,), Base.tail(cs)
114+
DiskIndex(size(i), ((maxtempind),), (outinds,), (tempinds,), (datainds,)), Base.tail(cs)
115115
end
116116

117117
function find_subranges_sorted(inds,allow_steprange=false)
@@ -150,7 +150,11 @@ function find_subranges_sorted(inds,allow_steprange=false)
150150
end
151151
end
152152
end
153-
push!(rangelist,inds[current_base]:last(inds))
153+
if current_step == 1 || current_step == 0
154+
push!(rangelist,inds[current_base]:last(inds))
155+
else
156+
push!(rangelist,inds[current_base]:current_step:last(inds))
157+
end
154158
push!(outputinds,current_base:length(inds))
155159
rangelist, outputinds
156160
end
@@ -174,8 +178,8 @@ function process_index(i::AbstractArray{<:Integer,N}, cs, s::SubRanges) where N
174178
(r,)
175179
end
176180
outinds = tuple.(outputinds)
177-
tempsize = maximum(length(rangelist))
178-
(length(i),), (tempsize,), (outinds,), (tempinds,), (datainds,), Base.tail(cs)
181+
tempsize = maximum(length,rangelist)
182+
DiskIndex((length(i),), (tempsize,), (outinds,), (tempinds,), (datainds,)), Base.tail(cs)
179183
else
180184
p = mysortperm(i)
181185
i_sorted = view(i,p)
@@ -190,7 +194,7 @@ function process_index(i::AbstractArray{<:Integer,N}, cs, s::SubRanges) where N
190194
(view(p,oi),)
191195
end
192196
tempsize = maximum(length(rangelist))
193-
size(i), (tempsize,), (outinds,), (tempinds,), (datainds,), Base.tail(cs)
197+
DiskIndex(size(i), (tempsize,), (outinds,), (tempinds,), (datainds,)), Base.tail(cs)
194198
end
195199
end
196200
function process_index(i::AbstractArray{Bool,N}, cs, cr::ChunkRead) where N
@@ -199,8 +203,14 @@ end
199203
function process_index(i::AbstractArray{Bool,N}, cs, cr::SubRanges) where N
200204
process_index(findall(i),cs,cr)
201205
end
202-
function process_index(i::StepRange{<:Integer}, cs, ::ChunkStrategy{CanStepRange})
203-
(length(i),), (length(i),), (Colon(),), (Colon(),), (i,), Base.tail(cs)
206+
function process_index(i::StepRange{<:Integer}, cs, ::ChunkRead{CanStepRange})
207+
DiskIndex((length(i),), (length(i),), ([(Colon(),)],), ([(Colon(),)],), ([(i,)],)), Base.tail(cs)
208+
end
209+
function process_index(i::StepRange{<:Integer}, cs, ::SubRanges{CanStepRange})
210+
DiskIndex((length(i),), (length(i),), ([(Colon(),)],), ([(Colon(),)],), ([(i,)],)), Base.tail(cs)
211+
end
212+
function process_index(i::StepRange{<:Integer}, cs, ::NoBatch{CanStepRange})
213+
DiskIndex((length(i),), (length(i),), (Colon(),), (Colon(),), (i,)), Base.tail(cs)
204214
end
205215
function process_index(i::AbstractArray{<:CartesianIndex{N},M}, cs, ::Union{ChunkRead,SubRanges}) where {N,M}
206216
csnow, csrem = splitcs(i,cs)
@@ -217,14 +227,16 @@ function process_index(i::AbstractArray{<:CartesianIndex{N},M}, cs, ::Union{Chun
217227
for (cI,a) in chunksdict
218228
datamin,datamax = extrema(first,a)
219229
aa = first.(a)
220-
tempind = aa .- datamin .+ oneunit(CartesianIndex{N})
230+
tempind = map(aa) do ind
231+
ind - datamin + oneunit(CartesianIndex{N})
232+
end
221233
push!(outinds, tuple(map(last,a)))
222234
push!(datainds, range.(datamin.I,datamax.I))
223235
push!(tempinds, tuple(tempind))
224236
s = datamax.I .- datamin.I .+ 1
225237
tempsize = max.(s,tempsize)
226238
end
227-
size(i), tempsize, (outinds,), (tempinds,), (datainds,), csrem
239+
DiskIndex(size(i), tempsize, (outinds,), (tempinds,), (datainds,)), csrem
228240
end
229241

230242

src/diskarray.jl

Lines changed: 107 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,10 @@ Determines a list of tuples used to perform the read or write operations. The re
4040
- `temp_indices` indices for reading from temp array
4141
- `data_indices` indices for reading from data array
4242
"""
43-
Base.@assume_effects :removable resolve_indices(a,i) = resolve_indices(a,i,batchstrategy(a))
44-
Base.@assume_effects :removable resolve_indices(a, i, batch_strategy) = _resolve_indices(eachchunk(a).chunks, i, (), (), (), (), (), batch_strategy)
45-
Base.@assume_effects :removable resolve_indices(a::AbstractVector, i::Tuple{AbstractVector{<:Integer}}, batch_strategy::NoBatch) = _resolve_indices(eachchunk(a).chunks, i, (), (), (), (), (), batch_strategy)
46-
Base.@assume_effects :removable resolve_indices(a::AbstractVector, i::Tuple{AbstractVector{<:Integer}}, batch_strategy::ChunkRead) = _resolve_indices(eachchunk(a).chunks, i, (), (), (), (), (), batch_strategy)
47-
Base.@assume_effects :removable resolve_indices(a::AbstractVector, i::Tuple{AbstractVector{<:Integer}}, batch_strategy::SubRanges) = _resolve_indices(eachchunk(a).chunks, i, (), (), (), (), (), batch_strategy)
48-
resolve_indices(a, ::Tuple{Colon}, _) = (length(a),), size(a), (Colon(),), (Colon(),), map(s->1:s,size(a))
43+
resolve_indices(a,i) = resolve_indices(a,i,batchstrategy(a))
44+
resolve_indices(a, i, batch_strategy) = _resolve_indices(eachchunk(a).chunks, i, DiskIndex((),(),(),(),()), batch_strategy)
45+
resolve_indices(a::AbstractVector, i::Tuple{AbstractVector{<:Integer}}, batch_strategy) = _resolve_indices(eachchunk(a).chunks, i, DiskIndex((), (), (), (), ()), batch_strategy)
46+
resolve_indices(a, ::Tuple{Colon}, _) = DiskIndex((length(a),), size(a), (Colon(),), (Colon(),), map(s->1:s,size(a)))
4947
resolve_indices(a, i::Tuple{<:CartesianIndex}, batch_strategy=NoBatch()) =
5048
resolve_indices(a, only(i).I, batch_strategy)
5149
function resolve_indices(a, i::Tuple{<:AbstractVector{<:Integer}}, batchstrategy)
@@ -73,49 +71,65 @@ function need_batch_index(i, cs, batchstrat)
7371
nb = (allow_multi || has_chunk_gap(approx_chunksize.(csnow), i)) && is_sparse_index(i; density_threshold=density_thresh)
7472
nb, csrem
7573
end
76-
function _resolve_indices(cs, i, output_size, temp_sizes, output_indices, temp_indices, data_indices, nb)
74+
75+
76+
struct DiskIndex{N,M,A<:Tuple,B<:Tuple,C<:Tuple}
77+
output_size::NTuple{N,Int}
78+
temparray_size::NTuple{M,Int}
79+
output_indices::A
80+
temparray_indices::B
81+
data_indices::C
82+
end
83+
@inline function merge_index(a::DiskIndex,b::DiskIndex)
84+
DiskIndex(
85+
(a.output_size...,b.output_size...),
86+
(a.temparray_size...,b.temparray_size...),
87+
(a.output_indices...,b.output_indices...),
88+
(a.temparray_indices...,b.temparray_indices...),
89+
(a.data_indices...,b.data_indices...),
90+
)
91+
end
92+
93+
function _resolve_indices(cs, i, indices_pre::DiskIndex, nb::ChunkStrategy)
7794
inow = first(i)
78-
outsize, tempsize, outinds, tempinds, datainds, cs = process_index(inow, cs, nb)
79-
output_size = (output_size..., outsize...)
80-
output_indices = (output_indices..., outinds...)
81-
temp_sizes = (temp_sizes..., tempsize...)
82-
temp_indices = (temp_indices..., tempinds...)
83-
data_indices = (data_indices..., datainds...)
84-
_resolve_indices(cs, Base.tail(i), output_size, temp_sizes, output_indices, temp_indices, data_indices, nb)
85-
end
86-
_resolve_indices(::Tuple{}, ::Tuple{}, output_size, temp_sizes, output_indices, temp_indices, data_indices, nb) = output_size, temp_sizes, output_indices, temp_indices, data_indices
95+
indices_new, cs_rem = process_index(inow, cs, nb)
96+
_resolve_indices(cs_rem, Base.tail(i), merge_index(indices_pre,indices_new), nb)
97+
end
98+
_resolve_indices(::Tuple{}, ::Tuple{}, indices::DiskIndex, nb::ChunkStrategy) = indices
8799
#No dimension left in array, only singular indices allowed
88-
function _resolve_indices(::Tuple{}, i, output_size, temp_sizes, output_indices, temp_indices, data_indices, nb)
100+
function _resolve_indices(::Tuple{}, i, indices_pre::DiskIndex, nb::ChunkStrategy)
89101
inow = first(i)
90102
(length(inow) == 1 && only(inow) == 1) || throw(ArgumentError("Trailing indices must be 1"))
91-
output_size = (output_size..., size(inow)...)
92-
output_indices = (output_indices..., size(inow)...)
93-
_resolve_indices((), Base.tail(i), output_size, temp_sizes, output_indices, temp_indices, data_indices, nb)
103+
indices_new = DiskIndex(size(inow),(),size(inow),(),())
104+
indices = merge_index(indices_pre,indices_new)
105+
_resolve_indices((), Base.tail(i), indices, nb)
94106
end
95107
#Still dimensions left, but no indices available
96-
function _resolve_indices(cs, ::Tuple{}, output_size, temp_sizes, output_indices, temp_indices, data_indices, nb)
108+
function _resolve_indices(cs, ::Tuple{}, indices_pre::DiskIndex, nb::ChunkStrategy)
97109
csnow = first(cs)
98110
arraysize_from_chunksize(csnow) == 1 || throw(ArgumentError("Indices can only be omitted for trailing singleton dimensions"))
99-
data_indices = (data_indices..., 1:1)
100-
temp_sizes = (temp_sizes..., 1)
101-
temp_indices = (temp_indices..., 1)
102-
_resolve_indices(Base.tail(cs), (), output_size, temp_sizes, output_indices, temp_indices, data_indices, nb)
111+
indices_new = add_dimension_index(nb)
112+
indices = merge_index(indices_pre,indices_new)
113+
_resolve_indices(Base.tail(cs), (), indices, nb)
103114
end
104115

116+
add_dimension_index(::NoBatch) = DiskIndex((),(1,),(),(1,),(1:1,))
117+
add_dimension_index(::Union{ChunkRead,SubRanges}) = DiskIndex((),(1,),([()],),([(1,)],),([(1:1,)],))
118+
105119

106120
#outsize, tempsize, outinds,tempinds,datainds,cs
107121
process_index(i, cs, ::NoBatch) = process_index(i, cs)
108-
process_index(inow::Integer, cs) = ((), 1, (), (1,), (inow:inow,), Base.tail(cs))
122+
process_index(inow::Integer, cs) = DiskIndex((), (1,), (), (1,), (inow:inow,)), Base.tail(cs)
109123
function process_index(::Colon, cs)
110124
s = arraysize_from_chunksize(first(cs))
111-
(s,), (s,), (Colon(),), (Colon(),), (1:s,), Base.tail(cs)
125+
DiskIndex((s,), (s,), (Colon(),), (Colon(),), (1:s,),), Base.tail(cs)
112126
end
113-
function process_index(i::AbstractUnitRange, cs)
114-
(length(i),), (length(i),), (Colon(),), (Colon(),), (i,), Base.tail(cs)
127+
function process_index(i::AbstractUnitRange{<:Integer}, cs, ::NoBatch)
128+
DiskIndex((length(i),), (length(i),), (Colon(),), (Colon(),), (i,)), Base.tail(cs)
115129
end
116130
function process_index(i::AbstractArray{<:Integer}, cs, ::NoBatch)
117131
indmin, indmax = extrema(i)
118-
size(i), ((indmax - indmin + 1),), map(_->Colon(),size(i)), ((i .- (indmin - 1)),), (indmin:indmax,), Base.tail(cs)
132+
DiskIndex(size(i), ((indmax - indmin + 1),), map(_->Colon(),size(i)), ((i .- (indmin - 1)),), (indmin:indmax,)), Base.tail(cs)
119133
end
120134
function process_index(i::AbstractArray{Bool,N}, cs, ::NoBatch) where {N}
121135
csnow, csrem = splitcs(i, cs)
@@ -124,7 +138,7 @@ function process_index(i::AbstractArray{Bool,N}, cs, ::NoBatch) where {N}
124138
indmin, indmax = cindmin.I, cindmax.I
125139
tempsize = indmax .- indmin .+ 1
126140
tempinds = view(i, range.(indmin, indmax)...)
127-
(sum(i),), tempsize, (Colon(),), (tempinds,), range.(indmin, indmax), csrem
141+
DiskIndex((sum(i),), tempsize, (Colon(),), (tempinds,), range.(indmin, indmax)), csrem
128142
end
129143
function process_index(i::AbstractArray{<:CartesianIndex{N}}, cs, ::NoBatch) where {N}
130144
csnow, csrem = splitcs(i, cs)
@@ -133,14 +147,14 @@ function process_index(i::AbstractArray{<:CartesianIndex{N}}, cs, ::NoBatch) whe
133147
indmin, indmax = cindmin.I, cindmax.I
134148
tempsize = indmax .- indmin .+ 1
135149
tempoffset = cindmin - oneunit(cindmin)
136-
tempinds = i .- tempoffset
150+
tempinds = i .- (CartesianIndex(tempoffset),)
137151
outinds = map(_->Colon(),size(i))
138-
size(i), tempsize, outinds, (tempinds,), range.(indmin, indmax), csrem
152+
DiskIndex(size(i), tempsize, outinds, (tempinds,), range.(indmin, indmax)), csrem
139153
end
140154
function process_index(i::CartesianIndices{N}, cs, ::NoBatch) where {N}
141155
_, csrem = splitcs(i, cs)
142156
cols = map(_ -> Colon(), i.indices)
143-
length.(i.indices), length.(i.indices), cols, cols, i.indices, csrem
157+
DiskIndex(length.(i.indices), length.(i.indices), cols, cols, i.indices), csrem
144158
end
145159
splitcs(i::AbstractArray{<:CartesianIndex}, cs) = splitcs(first(i).I, (), cs)
146160
splitcs(i::AbstractArray{Bool}, cs) = splitcs(size(i), (), cs)
@@ -149,6 +163,20 @@ splitcs(_, cs) = (first(cs),), Base.tail(cs)
149163
splitcs(si, csnow, csrem) = splitcs(Base.tail(si), (csnow..., first(csrem)), Base.tail(csrem))
150164
splitcs(::Tuple{}, csnow, csrem) = (csnow, csrem)
151165

166+
#Determine wether output and temp array can a) be identical b) share memory through reshape or
167+
# c) need to be allocated individually
168+
function output_aliasing(di::DiskIndex)
169+
if all(i->isa(i,Union{Int,AbstractUnitRange,Colon}),di.temparray_indices) &&
170+
all(i->isa(i,Union{Int,AbstractUnitRange,Colon}),di.output_indices)
171+
if di.output_size == di.temparray_size
172+
return :identical
173+
else
174+
return :reshapeoutput
175+
end
176+
else
177+
return :noalign
178+
end
179+
end
152180

153181

154182
function getindex_disk(a, i::Union{Integer,CartesianIndex}...)
@@ -162,6 +190,14 @@ function getindex_disk(a, i::Union{Integer,CartesianIndex}...)
162190
readblock!(a, outputarray, j...)
163191
only(outputarray)
164192
end
193+
function getindex_disk(a, i::Integer)
194+
checkscalar(i)
195+
checkbounds(a,i)
196+
outputarray = Array{eltype(a)}(undef, map(_ -> 1, size(a))...)
197+
j = map(k->k:k,CartesianIndices(a)[i].I)
198+
readblock!(a, outputarray, j...)
199+
only(outputarray)
200+
end
165201

166202
function create_outputarray(out, a, output_size)
167203
size(out) == output_size || throw(ArgumentError("Expected output array size of $output_size"))
@@ -171,17 +207,13 @@ create_outputarray(::Nothing, a, output_size) = Array{eltype(a)}(undef, output_s
171207

172208
getindex_disk(a, i...) = getindex_disk!(nothing, a, i...)
173209

174-
function _getindex_do_rest(out,a,output_size, temparray_size, output_indices, temparray_indices, data_indices)
175-
176-
end
177-
178210
function getindex_disk_batch!(out,a,i)
179-
output_size, temparray_size, output_indices, temparray_indices, data_indices = resolve_indices(a, i)
180-
moutput_indices = MRArray(output_indices)
181-
mtemparray_indices = MRArray(temparray_indices)
182-
mdata_indicess = MRArray(data_indices)
183-
outputarray = create_outputarray(out, a, output_size)
184-
temparray = Array{eltype(a)}(undef, temparray_size...)
211+
indices = resolve_indices(a, i)
212+
moutput_indices = MRArray(indices.output_indices)
213+
mtemparray_indices = MRArray(indices.temparray_indices)
214+
mdata_indicess = MRArray(indices.data_indices)
215+
outputarray = create_outputarray(out, a, indices.output_size)
216+
temparray = Array{eltype(a)}(undef, indices.temparray_size...)
185217
for ii in eachindex(moutput_indices)
186218
data_indices = mdata_indicess[ii]
187219
output_indices = moutput_indices[ii]
@@ -194,12 +226,20 @@ function getindex_disk_batch!(out,a,i)
194226
end
195227

196228
function getindex_disk_nobatch!(out,a,i)
197-
output_size, temparray_size, output_indices, temparray_indices, data_indices = resolve_indices(a, i, NoBatch(allow_steprange(a), 1.0))
229+
indices = resolve_indices(a, i, NoBatch(allow_steprange(a), 1.0))
198230
#@debug output_size, temparray_size, output_indices, temparray_indices, data_indices
199-
outputarray = create_outputarray(out, a, output_size)
200-
temparray = Array{eltype(a)}(undef, temparray_size...)
201-
readblock!(a, temparray, data_indices...)
202-
transfer_results!(outputarray, temparray, output_indices, temparray_indices)
231+
outputarray = create_outputarray(out, a, indices.output_size)
232+
outalias = output_aliasing(indices)
233+
if outalias === :identical
234+
readblock!(a, outputarray, indices.data_indices...)
235+
elseif outalias === :reshapeoutput
236+
temparray = reshape(outputarray,indices.temparray_size)
237+
readblock!(a, temparray, indices.data_indices...)
238+
else
239+
temparray = Array{eltype(a)}(undef, indices.temparray_size...)
240+
readblock!(a, temparray, indices.data_indices...)
241+
transfer_results!(outputarray, temparray, indices.output_indices, indices.temparray_indices)
242+
end
203243
outputarray
204244
end
205245

@@ -243,11 +283,11 @@ end
243283

244284
function setindex_disk_batch!(a,v,i)
245285
batch_strategy = batchstrategy(a)
246-
output_size, temparray_size, output_indices, temparray_indices, data_indices = resolve_indices(a, i, batch_strategy)
247-
moutput_indices = MRArray(output_indices)
248-
mtemparray_indices = MRArray(temparray_indices)
249-
mdata_indicess = MRArray(data_indices)
250-
temparray = Array{eltype(a)}(undef, temparray_size...)
286+
indices = resolve_indices(a, i, batch_strategy)
287+
moutput_indices = MRArray(indices.output_indices)
288+
mtemparray_indices = MRArray(indices.temparray_indices)
289+
mdata_indicess = MRArray(indices.data_indices)
290+
temparray = Array{eltype(a)}(undef, indices.temparray_size...)
251291
for (output_indices, temparray_indices, data_indices) in zip(moutput_indices, mtemparray_indices, mdata_indicess)
252292
transfer_results_write!(v, temparray, output_indices, temparray_indices)
253293
vtemparray = maybeshrink(temparray, a, data_indices)
@@ -256,10 +296,18 @@ function setindex_disk_batch!(a,v,i)
256296
end
257297

258298
function setindex_disk_nobatch!(a,v,i)
259-
output_size, temparray_size, output_indices, temparray_indices, data_indices = resolve_indices(a, i, NoBatch())
260-
temparray = Array{eltype(a)}(undef, temparray_size...)
261-
transfer_results_write!(v, temparray, output_indices, temparray_indices)
262-
writeblock!(a, temparray, data_indices...)
299+
indices = resolve_indices(a, i, NoBatch())
300+
outalias = output_aliasing(indices)
301+
if outalias === :identical
302+
writeblock!(a, v, indices.data_indices...)
303+
elseif outalias === :reshapeoutput
304+
temparray = reshape(v,indices.temparray_size)
305+
writeblock!(a, temparray, indices.data_indices...)
306+
else
307+
temparray = Array{eltype(a)}(undef, indices.temparray_size...)
308+
transfer_results_write!(v, temparray, indices.output_indices, indices.temparray_indices)
309+
writeblock!(a, temparray, indices.data_indices...)
310+
end
263311
end
264312

265313
function setindex_disk!(a::AbstractArray, v::AbstractArray, i...)
@@ -268,6 +316,7 @@ function setindex_disk!(a::AbstractArray, v::AbstractArray, i...)
268316
else
269317
setindex_disk_nobatch!(a,v,i)
270318
end
319+
v
271320
end
272321

273322
"Find the indices of elements containing integers in a Tuple"

src/util/testtypes.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ DiskArrays.haschunks(::AccessCountDiskArray) = DiskArrays.Chunked()
3434
DiskArrays.eachchunk(a::AccessCountDiskArray) = DiskArrays.GridChunks(a, a.chunksize)
3535
function DiskArrays.readblock!(a::AccessCountDiskArray, aout, i::OrdinalRange...)
3636
ndims(a) == length(i) || error("Number of indices is not correct")
37-
foreach(i) do r
37+
foreach(i) do r
3838
isa(r,AbstractUnitRange) || DiskArrays.allow_steprange(a) || error("StepRange passed although trait is false")
3939
end
4040
# println("reading from indices ", join(string.(i)," "))

0 commit comments

Comments
 (0)