Skip to content

Commit 655e5da

Browse files
committed
perf: optimize CDF variable access by removing redundant VVR caching and disabling chunking
1 parent 2a18c9a commit 655e5da

File tree

4 files changed

+47
-29
lines changed

4 files changed

+47
-29
lines changed

src/loading/variable.jl

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,10 @@ function variable(cdf::CDFDataset, name)
2626
record_dims = record_sizes(vdr)
2727
dims = (record_dims..., vdr.max_rec + 1)
2828
N = vdr.z_num_dims + 1
29-
vvrs, vvr_type = read_vvrs(vdr)
30-
compression = if !isempty(vvrs) # # vvr records is the ultimative source
31-
vvr_type == VVR_ ? NoCompression : variable_compression(vdr)
32-
else
33-
NoCompression
34-
end
3529
byte_swap = is_big_endian_encoding(cdf.cdr.encoding)
3630

3731
return CDFVariable{T, N, typeof(vdr), typeof(cdf)}(
38-
name, vdr, cdf, dims, vvrs, compression, byte_swap
32+
name, vdr, cdf, dims, byte_swap
3933
)
4034
end
4135

@@ -45,15 +39,20 @@ function DiskArrays.readblock!(var::CDFVariable{T, N}, dest::AbstractArray{T}, r
4539

4640
buffer = parent(var.parentdataset)
4741
RecordSizeType = recordsize_type(var.parentdataset)
48-
entries = var.vvrs
42+
entries, vvr_type = read_vvrs(var.vdr)
4943
isempty(entries) && return dest
44+
compression = if !isempty(entries) # # vvr records is the ultimative source
45+
vvr_type == VVR_ ? NoCompression : variable_compression(var.vdr)
46+
else
47+
NoCompression
48+
end
5049

5150
record_range = ranges[end]
5251
other_ranges = ranges[1:(N - 1)]
5352
dims_without_record = var.dims[1:(N - 1)]
5453

5554
is_full_record = length.(other_ranges) == dims_without_record
56-
is_no_compression = var.compression == NoCompression
55+
is_no_compression = compression == NoCompression
5756

5857
first_rec = first(record_range)
5958
last_rec = last(record_range)
@@ -87,7 +86,7 @@ function DiskArrays.readblock!(var::CDFVariable{T, N}, dest::AbstractArray{T}, r
8786
dest_view = selectdim(dest, N, dest_range)
8887
total_elems = record_size * length(entry)
8988
decompressor = take!(decompressors())
90-
load_cvvr_data!(dest_view, 1, buffer, entry.offset, total_elems, RecordSizeType, var.compression; decompressor)
89+
load_cvvr_data!(dest_view, 1, buffer, entry.offset, total_elems, RecordSizeType, compression; decompressor)
9190
put!(decompressors(), decompressor)
9291
else
9392
# partial entry
@@ -101,7 +100,7 @@ function DiskArrays.readblock!(var::CDFVariable{T, N}, dest::AbstractArray{T}, r
101100
load_vvr_data!(chunk, 1, buffer, entry.offset, total_elems, RecordSizeType)
102101
else
103102
decompressor = take!(decompressors())
104-
load_cvvr_data!(chunk, 1, buffer, entry.offset, total_elems, RecordSizeType, var.compression; decompressor)
103+
load_cvvr_data!(chunk, 1, buffer, entry.offset, total_elems, RecordSizeType, compression; decompressor)
105104
put!(decompressors(), decompressor)
106105
end
107106

src/variable.jl

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ struct CDFVariable{T, N, V, P} <: AbstractVariable{T, N}
1313
vdr::V
1414
parentdataset::P
1515
dims::NTuple{N, Int}
16-
vvrs::Vector{VVREntry}
17-
compression::CompressionType
1816
byte_swap::Bool
1917
end
2018

@@ -30,23 +28,39 @@ function dst_src_ranges(first, last, entry)
3028
return (dest_first:dest_last, local_first:local_last)
3129
end
3230

33-
DiskArrays.haschunks(::CDFVariable) = DiskArrays.Chunked()
34-
function DiskArrays.eachchunk(var::CDFVariable)
31+
# Codes seem to be faster if we disable chunking
32+
DiskArrays.haschunks(::CDFVariable) = DiskArrays.Unchunked()
33+
# DiskArrays.haschunks(::CDFVariable) = DiskArrays.Chunked()
34+
DiskArrays.eachchunk(var::CDFVariable) = _eachchunk(var)
35+
36+
function _eachchunk(var::CDFVariable)
37+
N = ndims(var)
38+
chunks = ntuple(N) do i
39+
arraysize = var.dims[i]
40+
chunksize = max(arraysize, 1) # handle zero-size dimensions
41+
DiskArrays.RegularChunks(chunksize, 0, arraysize)
42+
end
43+
return DiskArrays.GridChunks(chunks)
44+
end
45+
46+
function _eachchunk_vvrs(var::CDFVariable)
47+
vvrs, _ = read_vvrs(var.vdr)
3548
N = ndims(var)
3649
chunks = ntuple(N) do i
3750
if i != N
3851
DiskArrays.RegularChunks(var.dims[i], 0, var.dims[i])
3952
else
40-
chunksizes = length.(var.vvrs)
41-
if length(var.vvrs) > 0
42-
chunksizes[end] = @views var.dims[N] - sum(chunksizes[1:end-1])
53+
chunksizes = length.(vvrs)
54+
if length(vvrs) > 0
55+
chunksizes[end] = @views var.dims[N] - sum(chunksizes[1:(end - 1)])
4356
end
4457
DiskArrays.IrregularChunks(chunksizes = chunksizes)
4558
end
4659
end
4760
return DiskArrays.GridChunks(chunks)
4861
end
4962

63+
5064
function Base.getproperty(var::CDFVariable, name::Symbol)
5165
name in fieldnames(CDFVariable) && return getfield(var, name)
5266
if name == :attrib
@@ -77,4 +91,4 @@ function CPR(var::CDFVariable)
7791
return CPR(parent(cdf), vdr.cpr_or_spr_offset, recordsize_type(cdf))
7892
end
7993

80-
is_record_varying(v::CDFVariable) = is_record_varying(v.vdr)
94+
is_record_varying(v::CDFVariable) = is_record_varying(v.vdr)

test/perf_test.jl

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,29 @@ sum(var)
1414
full_load(elx_file)
1515
b0 = @b ds["elb_pef_hs_Epat_eflux"] evals=20
1616
b1= @b sum(Array(ds["elb_pef_hs_Epat_eflux"])) evals=5
17+
b12= @b sum(ds["elb_pef_hs_Epat_eflux"]) evals=5
1718
b2= @b full_load(elx_file) evals=2
1819

1920
mms_file = data_path(".mms1_scm_srvy_l2_scsrvy_20190301_v2.2.0.cdf")
2021
ds = CDFDataset(mms_file)
2122
sum(ds["mms1_scm_acb_gse_scsrvy_srvy_l2"])
23+
sum(ds["mms1_scm_acb_gse_scsrvy_srvy_l2"][:, 100:100000])
2224
b30 = @b ds["mms1_scm_acb_gse_scsrvy_srvy_l2"] evals=20
2325
b3= @b sum(Array(ds["mms1_scm_acb_gse_scsrvy_srvy_l2"])) evals=2
2426
b4= @b sum(ds["mms1_scm_acb_gse_scsrvy_srvy_l2"][:, 100:100000]) evals=5
2527
b5= @b full_load(mms_file) evals=2
2628

27-
b = [b0, b1, b2, b30, b3, b4, b5]
29+
b = [b0, b1, b12, b2, b30, b3, b4, b5]
2830
@info "Benchmarks" b
2931

3032
# ┌ Info: Benchmarks
3133
# │ b =
32-
# │ 7-element Vector{Chairmarks.Sample}:
33-
# │ 629.150 ns (9 allocs: 784 bytes)
34-
# │ 2.142 μs (25 allocs: 29.328 KiB)
35-
# │ 87.333 μs (3864 allocs: 169.844 KiB)
36-
# │ 2.410 μs (15 allocs: 26.828 KiB)
37-
# │ 9.632 ms (585 allocs: 31.692 MiB)
38-
# │ 474.792 μs (111 allocs: 1.381 MiB)
39-
# └ 273.312 μs (276 allocs: 46.094 KiB)
34+
# │ 8-element Vector{Chairmarks.Sample}:
35+
# │ 539.550 ns (6 allocs: 528 bytes)
36+
# │ 2.083 μs (20 allocs: 29.172 KiB)
37+
# │ 2.000 μs (24 allocs: 29.328 KiB)
38+
# │ 83.896 μs (3777 allocs: 162.469 KiB)
39+
# │ 385.400 ns (7 allocs: 528 bytes)
40+
# │ 9.586 ms (574 allocs: 31.655 MiB)
41+
# │ 467.275 μs (100 allocs: 1.344 MiB)
42+
# └ 20.855 μs (250 allocs: 12.484 KiB)

test/runtests.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ end
5757
@test ds["var_string"] == reference["var_string"]
5858

5959
var = ds["var"]
60+
#TODO: find a better and small dataset to really test the chunking
61+
@test CommonDataFormat._eachchunk_vvrs(ds["var3d"]) == CommonDataFormat._eachchunk(ds["var3d"])
6062
@test occursin("compressed", string(var.vdr))
6163
end
6264

@@ -89,4 +91,4 @@ end
8991
@test string(TT2000(0)) == "2000-01-01T11:58:55.816"
9092
@test TT2000(0) == TT2000(0) |> bswap
9193
@test TT2000(0) == DateTime("2000-01-01T11:58:55.816")
92-
end
94+
end

0 commit comments

Comments
 (0)