Skip to content

Commit e271554

Browse files
authored
feat: more Datetime methods for Epoch (#23)
* refactor: improve invalidation by replacing sizeof with custom _sizeof implementation * feat: more Datetime methods for Epoch
1 parent 7754121 commit e271554

File tree

11 files changed

+151
-21
lines changed

11 files changed

+151
-21
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4"
1010
DiskArrays = "3c3547ce-8d99-4f5e-a174-61eb10b00ae3"
1111
LibDeflate = "9255714d-24a7-4b30-8ea3-d46a97f7e13b"
1212
Mmap = "a63ad114-7e13-5084-954f-fe012c677804"
13+
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
1314
ResumableFunctions = "c5292f4c-5179-55e1-98c5-05642aab7184"
1415
StaticStrings = "4db0a0c5-418a-4e1d-8806-cb305fe13294"
1516

@@ -27,6 +28,7 @@ Dictionaries = "0.4"
2728
DiskArrays = "0.4"
2829
LibDeflate = "0.4.3"
2930
Mmap = "1"
31+
PrecompileTools = "1"
3032
ResumableFunctions = "1"
3133
StaticStrings = "0.2.6"
3234
julia = "1.10"

justfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
perf:
2+
#!/usr/bin/env -S julia --threads=auto --project=.
3+
@time using CommonDataFormat
4+
elx_file = "data/elb_l2_epdef_20210914_v01.cdf"
5+
@time ds = CDFDataset(elx_file)
6+
@time var = ds["elb_pef_hs_Epat_eflux"]
7+
@time Array(var)
8+
@time var2 = ds["elb_pef_hs_epa_spec"]
9+
@time Array(var2)
10+
@time Array(ds["elb_pef_fs_time"])
11+
12+
snoop:
13+
#!/usr/bin/env -S julia --threads=auto --project=. -i
14+
using SnoopCompileCore
15+
invs = @snoop_invalidations using CommonDataFormat
16+
using SnoopCompile, AbstractTrees
17+
trees = invalidation_trees(invs)

src/CommonDataFormat.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ using Base.Threads
1010
using CodecZlib: GzipDecompressor, transcode
1111
using LibDeflate
1212
using LibDeflate: GzipDecompressResult
13+
using PrecompileTools
1314

1415
export CDFDataset, CDFVariable
1516
export Majority, CompressionType, DataType
@@ -29,5 +30,6 @@ include("vattribute.jl")
2930
include("dataset.jl")
3031
include("loading/attribute.jl")
3132
include("loading/variable.jl")
33+
include("precompile.jl")
3234

33-
end
35+
end

src/epochs.jl

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# 3. CDF_TIME_TT2000 (TT2000 as short) is nanoseconds since J2000 with leap seconds
77

88
import Base: promote_rule, -, +
9+
using Dates: value, toms, tons
910

1011
include("leap_second.jl")
1112

@@ -14,6 +15,20 @@ const EPOCH_OFFSET_SECONDS = 62167219200.0 # Seconds from year 0 to Unix epoch
1415

1516
abstract type CDFDateTime <: Dates.AbstractDateTime end
1617

18+
struct Picosecond <: Period
19+
value::Float64
20+
end
21+
22+
Picosecond(ns::Nanosecond) = convert(Picosecond, ns)
23+
Picosecond(p::Period) = Picosecond(Nanosecond(p))
24+
Base.convert(::Type{Nanosecond}, x::Picosecond) = Nanosecond(round(Int64, x.value / 1.0e3))
25+
Base.convert(::Type{Picosecond}, x::Nanosecond) = Picosecond(x.value * 1.0e3)
26+
Base.convert(::Type{Picosecond}, x::Dates.FixedPeriod) = Picosecond(Nanosecond(x))
27+
Base.promote_rule(::Type{Picosecond}, ::Type{<:Dates.FixedPeriod}) = Picosecond
28+
29+
Dates._units(x::Picosecond) = " picosecond" * (abs(value(x)) == 1 ? "" : "s")
30+
31+
1732
"""
1833
Epoch
1934
@@ -56,8 +71,11 @@ fillvalue(::Epoch16) = -1.0e31
5671
fillvalue(::TT2000) = 9999
5772

5873
(-)(epoch::Epoch, other::Epoch) = Millisecond(round(Int64, epoch.instant - other.instant))
59-
(+)(tt2000::TT2000, other::Period) = TT2000(tt2000.instant.value + Dates.tons(other))
60-
(+)(epoch::Epoch, other::Period) = Epoch(epoch.instant + Dates.toms(other))
74+
(-)(epoch::Epoch16, other::Epoch16) = Picosecond((epoch.seconds - other.seconds) * 1.0e12 + epoch.picoseconds - other.picoseconds)
75+
(+)(tt2000::TT2000, other::Period) = TT2000(value(tt2000) + tons(other))
76+
(-)(tt2000::TT2000, other::Period) = TT2000(value(tt2000) - tons(other))
77+
(+)(epoch::Epoch, other::Period) = Epoch(value(epoch) + toms(other))
78+
(-)(epoch::Epoch, other::Period) = Epoch(value(epoch) - toms(other))
6179

6280
# Conversion to DateTime
6381
function Dates.DateTime(epoch::Epoch)
@@ -66,8 +84,8 @@ end
6684

6785
function Dates.DateTime(epoch::Epoch16)
6886
s_since_unix = epoch.seconds - EPOCH_OFFSET_SECONDS
69-
total_ns = s_since_unix * 1.0e9 + epoch.picoseconds / 1000.0
70-
return DateTime(1970) + Nanosecond(round(Int64, total_ns))
87+
total_ms = s_since_unix * 1.0e3 + epoch.picoseconds / 1.0e9
88+
return DateTime(1970) + Millisecond(round(Int64, total_ms))
7189
end
7290

7391
function Dates.DateTime(epoch::TT2000)
@@ -79,10 +97,10 @@ end
7997

8098
# Conversion from TimeType
8199
function Epoch16(dt::DateTime)
82-
ns_since_unix = (dt - DateTime(1970, 1, 1)).value * 1_000_000 # DateTime precision is milliseconds
83-
s_since_unix = ns_since_unix / 1.0e9
100+
ms_since_unix = value(dt - DateTime(1970, 1, 1))
101+
s_since_unix = div(ms_since_unix, 1000)
84102
s_total = s_since_unix + EPOCH_OFFSET_SECONDS
85-
ps_component = (ns_since_unix % 1.0e9) * 1000.0 # Convert nanoseconds remainder to picoseconds
103+
ps_component = rem(ms_since_unix, 1000) * 1000000000 # Convert nanoseconds remainder to picoseconds
86104
return Epoch16(s_total, ps_component)
87105
end
88106

@@ -102,11 +120,12 @@ for f in (:year, :month, :day, :hour, :minute, :second, :millisecond)
102120
@eval Dates.$f(epoch::CDFDateTime) = Dates.$f(DateTime(epoch))
103121
end
104122

105-
Dates.value(epoch::CDFDateTime) = epoch.instant
123+
Dates.value(epoch::Epoch) = epoch.instant
124+
Dates.value(epoch::Epoch16) = ComplexF64(epoch.seconds, epoch.picoseconds)
106125
Dates.value(epoch::TT2000) = epoch.instant.value
107126

108127
function Base.floor(x::T, p::Union{DatePeriod, TimePeriod}) where {T <: CDFDateTime}
109-
convert(T, floor(convert(DateTime, x), p))
128+
return convert(T, floor(convert(DateTime, x), p))
110129
end
111130

112131
function Base.show(io::IO, epoch::CDFDateTime)
@@ -117,7 +136,12 @@ function Base.show(io::IO, epoch::CDFDateTime)
117136
print(io, DateTime(epoch))
118137
end
119138
end
139+
function Base.show(io::IO, epoch::Epoch16)
140+
return print(io, DateTime(epoch))
141+
end
142+
120143
Base.promote_rule(::Type{<:CDFDateTime}, ::Type{Dates.DateTime}) = Dates.DateTime
121144
Base.convert(::Type{Dates.DateTime}, x::CDFDateTime) = Dates.DateTime(x)
122-
Base.bswap(x::T) where {T <: CDFDateTime} = T(Base.bswap(x.instant))
145+
Base.bswap(x::Epoch) = Epoch(Base.bswap(x.instant))
146+
Base.bswap(x::Epoch16) = Epoch16(Base.bswap(x.seconds), Base.bswap(x.picoseconds))
123147
Base.bswap(x::TT2000) = TT2000(Base.bswap(x.instant.value))

src/parsing.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ end
2525
end
2626

2727
@inline function read_be_i(v::Vector{UInt8}, i, T::Base.DataType)
28-
return read_be(v, i, T), i + sizeof(T)
28+
return read_be(v, i, T), i + _sizeof(T)
2929
end
3030

3131
@inline function read_be_i(v::Vector{UInt8}, i, n::Integer, T)
@@ -61,7 +61,7 @@ macro read_be_fields(buffer, pos, Ts...)
6161
for (sym, T) in zip(value_syms, types)
6262
Tesc = esc(T)
6363
push!(stmts, :(local $sym = read_be($buf, $pos_sym, $Tesc)))
64-
push!(stmts, :($pos_sym += sizeof($Tesc)))
64+
push!(stmts, :($pos_sym += _sizeof($Tesc)))
6565
end
6666

6767
tuple_expr = Expr(:tuple, value_syms...)
@@ -83,7 +83,7 @@ end
8383
for (i, idx) in enumerate(indxs)
8484
T = fieldtype(SType, idx)
8585
push!(exprs, :(local $(value_syms[i]) = read_be(buffer, $pos_sym, $T)))
86-
push!(exprs, :($pos_sym += sizeof($T)))
86+
push!(exprs, :($pos_sym += _sizeof($T)))
8787
end
8888

8989
# Return tuple of values and final position

src/precompile.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
precompile(Array, (CDFVariable{TT2000, 1, VDR{Int64}, CDFDataset{NoCompression, Int64}},))
2+
for T in (Float32, Float64), i in 1:3
3+
precompile(Array, (CDFVariable{T, i, VDR{Int64}, CDFDataset{NoCompression, Int64}},))
4+
end
5+
6+
PrecompileTools.@setup_workload begin
7+
elx_file = joinpath(@__DIR__, "../data/elb_l2_epdef_20210914_v01.cdf")
8+
9+
PrecompileTools.@compile_workload begin
10+
ds = CDFDataset(elx_file)
11+
end
12+
end

src/types.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@ abstract type Record end
22

33
abstract type ReservedField end
44
struct RInt32 <: ReservedField end
5-
Base.sizeof(::Type{RInt32}) = sizeof(Int32)
5+
6+
_sizeof(x) = sizeof(x)
7+
_sizeof(::Type{RInt32}) = sizeof(Int32)

test/epochs_test.jl

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,49 @@
11
using Test
22
using CommonDataFormat
3+
import CommonDataFormat as CDF
34
using Dates
45

56
@testset "Epochs" begin
6-
@test Epoch(DateTime(0)) == Epoch(0)
7+
t = Epoch(DateTime(0))
8+
@test t == Epoch(0)
79
@test DateTime(Epoch(DateTime(0))) == DateTime(0)
810
@test Epoch(Epoch(0)) == Epoch(0)
911
@test Epoch(10) - Epoch(0) == Millisecond(10)
1012
@test string(Epoch(-1.0e31)) == "FILLVAL"
13+
@test Epoch(10) - Millisecond(10) == Epoch(0)
14+
@test Epoch(0) + Second(1) == Epoch(1000)
15+
@test ntoh(hton(t)) == t
1116
# @test Epoch16(DateTime(0)) == Epoch16(0, 0)
1217
end
1318

1419
@testset "TT2000" begin
15-
@test DateTime(TT2000(DateTime(2000))) == DateTime(2000)
20+
t = TT2000(DateTime(2000))
21+
@test DateTime(t) == DateTime(2000)
1622
@test TT2000(DateTime(TT2000(0))) == TT2000(0)
1723
@test TT2000(TT2000(0)) == TT2000(0)
1824
@test TT2000(10) - TT2000(0) == Nanosecond(10)
25+
@test t - Day(1) == DateTime(1999, 12, 31)
1926
@test floor(TT2000(0), Minute(1)) == DateTime(2000, 1, 1, 11, 58)
2027
@test TT2000(0) + Minute(1) == TT2000(60_000_000_000)
2128

2229
@test string(TT2000(0)) == "2000-01-01T11:58:55.816"
2330
@test TT2000(0) == TT2000(0) |> bswap
2431
@test TT2000(0) == DateTime("2000-01-01T11:58:55.816")
25-
end
32+
end
33+
34+
@testset "Epoch16" begin
35+
t = Epoch16(6.377810224e10, 8.97e11)
36+
@test t == DateTime(2021, 1, 17, 11, 30, 40, 897)
37+
@test Epoch16(DateTime(t)) == t
38+
@test string(t) == "2021-01-17T11:30:40.897"
39+
@test ntoh(hton(t)) == t
40+
@test Epoch16(6.377810224e10, 8.97e11) - Epoch16(6.377810224e10, 0) == CDF.Picosecond(8.97e11)
41+
end
42+
43+
@testset "Picosecond" begin
44+
@test CDF.Picosecond(1) == CDF.Picosecond(1)
45+
@test Nanosecond(CDF.Picosecond(Nanosecond(1000))) == Nanosecond(1000)
46+
@test string(CDF.Picosecond(1)) == "1.0 picosecond"
47+
@test CDF.Picosecond(Millisecond(1)) == CDF.Picosecond(1.0e9)
48+
@test CDF.Picosecond(1.0e9) == Millisecond(1)
49+
end

test/perf_test.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ b2= @b full_load(elx_file) evals=2
1919

2020
mms_file = data_path(".mms1_scm_srvy_l2_scsrvy_20190301_v2.2.0.cdf")
2121
ds = CDFDataset(mms_file)
22-
sum(ds["mms1_scm_acb_gse_scsrvy_srvy_l2"])
23-
sum(ds["mms1_scm_acb_gse_scsrvy_srvy_l2"][:, 100:100000])
22+
var = ds["mms1_scm_acb_gse_scsrvy_srvy_l2"]
23+
sum(var)
24+
sum(var[:, 100:100000])
2425
b30 = @b ds["mms1_scm_acb_gse_scsrvy_srvy_l2"] evals=20
2526
b3= @b sum(Array(ds["mms1_scm_acb_gse_scsrvy_srvy_l2"])) evals=2
2627
b4= @b sum(ds["mms1_scm_acb_gse_scsrvy_srvy_l2"][:, 100:100000]) evals=5

test/perf_test_ntoh.jl

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
a = rand(3, 100000)
2+
3+
f1(a) = map!(ntoh, a, a)
4+
f2(a) = a .= ntoh.(a)
5+
6+
using Polyester
7+
8+
function f3(a)
9+
return @inbounds @simd for i in eachindex(a)
10+
a[i] = ntoh(a[i])
11+
end
12+
end
13+
14+
function f4(a)
15+
@batch for i in eachindex(a)
16+
a[i] = ntoh(a[i])
17+
end
18+
return a
19+
end
20+
21+
using Base.Threads
22+
23+
function f5(a)
24+
Threads.@threads for i in eachindex(a)
25+
a[i] = ntoh(a[i])
26+
end
27+
return a
28+
end
29+
30+
a = rand(3, 10000000)
31+
32+
b1 = @b f1(a) evals = 10
33+
b2 = @b f2(a) evals = 10
34+
b3 = @b f3(a) evals = 10
35+
b4 = @b f4(a) evals = 10
36+
b5 = @b f5(a) evals = 10
37+
38+
39+
ds = CDFDataset("/Users/zijin/.cdaweb/data/THB_L2_FGM/thb_fgl_gseQ_thb_l2s_fgm_20210120000000_20210120235959_cdaweb.cdf")
40+
var = ds["thb_fgl_epoch16"]
41+
@b Array(var)

0 commit comments

Comments
 (0)