何がしたいのか
任意の型を「一時的なデータ」として取りまわすことを目的としています。特に、さまざまなところでデータを使い回すときに、「すでに確保している」「確保していない」がわかるようにしたいです。確保しているデータを使おうとするとエラーが出るようにしたいです。
この記事が役に立つこと
- 具体的な独自型の定義の例
- 不必要なメモリアロケーションをしないデータの取り回し
背景
格子量子色力学のシミュレーションを行うためのオープンソースソフトウェア群JuliaQCDを開発しています。この中の一つに、4次元の格子上に定義されたゲージ場を扱うパッケージGaugefields.jlがあります。一つのゲージ場は4次元の格子上に定義された複素3x3ユニタリー行列で表現されており、非常に大きな配列です。ですので、新しい配列を毎回定義しているとすごく時間がかかります。ですので、あらかじめ「一時的なデータ」を最初の方で確保しておき、それを使い回すことで計算を高速化できます。
現在のGaugefields.jlでは、一時データはゲージ場を要素とするVector
として定義されています。必要に応じて複数の一時ゲージ場をここから取り出して使います。しかし、この一時ゲージ場に入っていて後で使うデータと、上書きしてしまってもいいデータがどれかがわかりにくく、コードを書く際にデバッグが困難になっていました。これを解決することを目的としています。
コード
コードは一つのmoduleとしました。
module Temporalfields_module
mutable struct Temporalfields{TG}
_data::Vector{TG}
_flagusing::Vector{Bool}
_indices::Vector{Int64}
Nmax::Int64
function Temporalfields(a::TG; num=1, Nmax=1000) where {TG}
_data = Vector{TG}(undef, num)
_flagusing = zeros(Bool, num)
_indices = zeros(Int64, num)
for i = 1:num
_data[i] = similar(a)
end
return new{TG}(_data, _flagusing, _indices, Nmax)
end
end
Base.eltype(::Type{Temporalfields{TG}}) where {TG} = TG
Base.length(t::Temporalfields{TG}) where {TG} = length(t._data)
Base.size(t::Temporalfields{TG}) where {TG} = size(t._data)
function Base.firstindex(t::Temporalfields{TG}) where {TG}
return 1
end
function Base.lastindex(t::Temporalfields{TG}) where {TG}
return length(t._data)
end
function Base.getindex(t::Temporalfields{TG}, i::Int) where {TG}
if i > length(t._data)
@warn "The length of the temporalfields is shorter than the index $i. New temporal fields are created."
ndiff = i - length(t._data)
@assert i <= t.Nmax "The number of the tempralfields $i is larger than the maximum number $(Nmax). Change Nmax."
for n = 1:ndiff
push!(t._data, similar(t._data[1]))
push!(t._flagusing, 0)
push!(t._indices, 0)
end
end
if t._indices[i] == 0
index = findfirst(x -> x == 0, t._flagusing)
t._flagusing[index] = true
t._indices[i] = index
else
error("This index $i is being using. You should pay attention")
end
return t._data[t._indices[i]]
end
function Base.getindex(t::Temporalfields{TG}, I::Vararg{Int,N}) where {TG,N}
data = TG[]
for i in I
push!(data, t[i])
end
return data
end
function Base.getindex(t::Temporalfields{TG}, I::AbstractVector{T}) where {TG,T<:Integer}
data = TG[]
for i in I
push!(data, t[i])
end
return data
end
function Base.display(t::Temporalfields{TG}) where {TG}
n = length(t._data)
println("The total number of fields: $n")
numused = sum(t._flagusing)
println("The total number of fields used: $numused")
for i = 1:n
if t._indices[i] != 0
#println("The address $i is used as the index $(t._indices[i])")
println("The address $(t._indices[i]) is used as the index $i")
end
end
println("The flags: $(t._flagusing)")
println("The indices: $(t._indices)")
end
function unused!(t::Temporalfields{TG}, i) where {TG}
if t._indices[i] != 0
index = t._indices[i]
t._flagusing[index] = false
t._indices[i] = 0
end
end
function unused!(t::Temporalfields{TG}, I::AbstractVector{T}) where {TG,T<:Integer}
for i in I
unused!(t, i)
end
end
function unused!(t::Temporalfields{TG}) where {TG}
for i = 1:length(t)
unused!(t, i)
end
end
export Temporalfields, unused!
end
使用例
このTemporalfields
型の使用例は以下のとおりです。
include("Temporalfields.jl")
using .Temporalfields_module
function main()
a = rand(10)
tempvec = Temporalfields(a)
display(tempvec)
b = tempvec[1]
println(b)
display(tempvec)
c = tempvec[2]
println(c)
display(tempvec)
tempvec2 = Temporalfields(a; num=4)
d1 = tempvec2[1]
println(d1)
display(tempvec2)
println("---------------------")
d2 = tempvec2[2]
println(d2)
display(tempvec2)
unused!(tempvec2, 2)
println("---------------------")
d3 = tempvec2[3]
println(d3)
display(tempvec2)
println("---------------------")
d6 = tempvec2[6]
println(d6)
display(tempvec2)
println("---------------------")
vecd = tempvec2[3:10]
end
main()
これを実行すると、
The total number of fields: 1
The total number of fields used: 0
The flags: Bool[0]
The indices: [0]
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
The total number of fields: 1
The total number of fields used: 1
The address 1 is used as the index 1
The flags: Bool[1]
The indices: [1]
┌ Warning: The length of the temporalfields is shorter than the index 2. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
[2.250178182e-314, 2.38982809e-314, 2.25017819e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314, 2.343058413e-314, 2.3898280977e-314, 2.343058413e-314, 2.3898280977e-314]
The total number of fields: 2
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1]
The indices: [1, 2]
[2.250178182e-314, 2.38982809e-314, 2.25017819e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314, 2.343058413e-314, 2.3898280977e-314, 2.343058413e-314, 2.3898280977e-314]
The total number of fields: 4
The total number of fields used: 1
The address 1 is used as the index 1
The flags: Bool[1, 0, 0, 0]
The indices: [1, 0, 0, 0]
---------------------
[2.250178182e-314, 2.38982809e-314, 2.25017819e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314, 2.1569822157e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314]
The total number of fields: 4
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1, 0, 0]
The indices: [1, 2, 0, 0]
---------------------
[2.250178182e-314, 2.38982809e-314, 2.25017819e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314, 2.1569822157e-314, 2.38982809e-314, 2.343058413e-314, 2.3898280977e-314]
The total number of fields: 4
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 3
The flags: Bool[1, 1, 0, 0]
The indices: [1, 0, 2, 0]
---------------------
┌ Warning: The length of the temporalfields is shorter than the index 6. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
The total number of fields: 6
The total number of fields used: 3
The address 1 is used as the index 1
The address 2 is used as the index 3
The address 3 is used as the index 6
The flags: Bool[1, 1, 1, 0, 0, 0]
The indices: [1, 0, 2, 0, 0, 3]
---------------------
ERROR: LoadError: This index 3 is being using. You should pay attention
となります。通常の配列のように
tempvec2 = Temporalfields(a; num=4)
d1 = tempvec2[1]
とすると、一時データの1番を使います。ただし、1番がすでに使われているときは、エラーを出して終了します。使われていないときは、一時データの中で使っていない要素を取り出してきて、それを1番とします。また、
unused!(tempvec2,1)
とすると、一時データの1番を解放します。つまり、自分で一時データを確保して、使い終わった後に別の用途で使用したいときにはunused!
で解放してあげることになります。このunused!
を使う方法によって、不用意に意図しない上書きを防ぎます。これは、定義するときにだけ使えるので
d1 = tempvec2[1]
d2 = tempvec2[2]
d3 = tempvec2[3]
や
d = tempvec2[1:4]
のように定義してあげた後は、通常のデータとして使用することができます。
改良
毎回インデックスを指定するのは、少し面倒です。そこで、
function get_temp(t::Temporalfields{TG}) where {TG}
n = length(t._data)
i = findfirst(x -> x == 0, t._indices)
if i == nothing
@warn "All temporal fields are used. New one is created."
i = n + 1
end
return t[i], i
end
function get_temp(t::Temporalfields{TG}, num) where {TG}
n = length(t._data)
i_s = Int64[]
t_s = TG[]
for k = 1:num
tk, i = get_temp(t)
push!(t_s, tk)
push!(i_s, i)
end
return t_s, i_s
end
export Temporalfields, unused!, get_temp
という関数を実装しました。これにより、
function main2()
a = rand(10)
tempvec = Temporalfields(a)
display(tempvec)
b, it_b = get_temp(tempvec)
println(b)
display(tempvec)
c, it_c = get_temp(tempvec)
println(c)
display(tempvec)
tempvec2 = Temporalfields(a; num=4)
d1, it_d1 = get_temp(tempvec2)
println(d1)
display(tempvec2)
println("---------------------")
d2, it_d2 = get_temp(tempvec2)
println(d2)
display(tempvec2)
unused!(tempvec2, it_d2)
println("---------------------")
#d3 = tempvec2[3]
d3, it_d3 = get_temp(tempvec2)
println(d3)
display(tempvec2)
println("---------------------")
vecd, its_vecd = get_temp(tempvec2, 7)
display(tempvec2)
unused!(tempvec2, its_vecd[3])
unused!(tempvec2, its_vecd[7])
display(tempvec2)
unused!(tempvec2, its_vecd)
display(tempvec2)
end
main2()
の実行結果は、
The total number of fields: 1
The total number of fields used: 0
The flags: Bool[0]
The indices: [0]
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
The total number of fields: 1
The total number of fields used: 1
The address 1 is used as the index 1
The flags: Bool[1]
The indices: [1]
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 2. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
[2.2338109335e-314, 2.2338130204e-314, 2.233810965e-314, 2.233813036e-314, 2.2338109967e-314, 2.2411677607e-314, 2.2338110283e-314, 2.23381106e-314, 2.2338110916e-314, 2.2411677686e-314]
The total number of fields: 2
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1]
The indices: [1, 2]
[2.349793168e-314, 2.34979765e-314, 2.349793168e-314, 2.34979765e-314, 2.34979765e-314, 2.34979765e-314, 2.3497931996e-314, 2.3497931996e-314, 2.2338106647e-314, 2.34979765e-314]
The total number of fields: 4
The total number of fields used: 1
The address 1 is used as the index 1
The flags: Bool[1, 0, 0, 0]
The indices: [1, 0, 0, 0]
---------------------
[2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.231098837e-314, 2.231098932e-314, 2.32407147e-314, 2.32407147e-314]
The total number of fields: 4
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1, 0, 0]
The indices: [1, 2, 0, 0]
---------------------
[2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.32407147e-314, 2.231098837e-314, 2.231098932e-314, 2.32407147e-314, 2.32407147e-314]
The total number of fields: 4
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1, 0, 0]
The indices: [1, 2, 0, 0]
---------------------
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 5. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 6. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 7. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 8. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
┌ Warning: All temporal fields are used. New one is created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:92
┌ Warning: The length of the temporalfields is shorter than the index 9. New temporal fields are created.
└ @ Main.Temporalfields_module ~/test/tempvec/Temporalfields.jl:37
The total number of fields: 9
The total number of fields used: 9
The address 1 is used as the index 1
The address 2 is used as the index 2
The address 3 is used as the index 3
The address 4 is used as the index 4
The address 5 is used as the index 5
The address 6 is used as the index 6
The address 7 is used as the index 7
The address 8 is used as the index 8
The address 9 is used as the index 9
The flags: Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]
The indices: [1, 2, 3, 4, 5, 6, 7, 8, 9]
The total number of fields: 9
The total number of fields used: 7
The address 1 is used as the index 1
The address 2 is used as the index 2
The address 3 is used as the index 3
The address 4 is used as the index 4
The address 6 is used as the index 6
The address 7 is used as the index 7
The address 8 is used as the index 8
The flags: Bool[1, 1, 1, 1, 0, 1, 1, 1, 0]
The indices: [1, 2, 3, 4, 0, 6, 7, 8, 0]
The total number of fields: 9
The total number of fields used: 2
The address 1 is used as the index 1
The address 2 is used as the index 2
The flags: Bool[1, 1, 0, 0, 0, 0, 0, 0, 0]
The indices: [1, 2, 0, 0, 0, 0, 0, 0, 0]
となります。