gpt4 book ai didi

julia - 在 Julia 中将函数参数更改为关键字似乎会引入类型不稳定

转载 作者:行者123 更新时间:2023-12-02 03:33:34 24 4
gpt4 key购买 nike

我有一个程序,其中 main() 函数采用四个参数。当我在函数上运行 @code_warntype 时,似乎没有什么问题。所有变量都指定了类型,并且没有 UNION 实例或其他明显的警告标志。

抱歉,该程序相当长,但我不知道如何在保留问题的同时缩短它:

function main(n::Int, dice::Int=6, start::Int=1, modal::Int=3) ::Tuple{String, Vector{String}, Vector{Float64}}
board = String["GO", "A1", "CC1", "A2", "T1", "R1", "B1", "CH1", "B2", "B3",
"JAIL", "C1", "U1", "C2", "C3", "R2", "D1", "CC2", "D2", "D3",
"FP", "E1", "CH2", "E2", "E3", "R3", "F1", "F2", "U2", "F3",
"G2J", "G1", "G2", "CC3", "G3", "R4", "CH3", "H1", "T2", "H2"]
cc_cards = shuffle(collect(1:16))
ch_cards = shuffle(collect(1:16))
function take_cc_card(square::Int, cards::Vector{Int})::Tuple{Int, Vector{Int}}
if cards[1] == 1
square = findfirst(board, "GO")
elseif cards[1] == 2
square = findfirst(board, "JAIL")
end
p = pop!(cards)
unshift!(cards, p)
return square, cards
end
function take_ch_card(square::Int, cards::Vector{Int})::Tuple{Int, Vector{Int}}
if cards[1] == 1
square = findfirst(board, "GO")
elseif cards[1] == 2
square = findfirst(board, "JAIL")
elseif cards[1] == 3
square = findfirst(board, "C1")
elseif cards[1] == 4
square = findfirst(board, "E3")
elseif cards[1] == 5
square = findfirst(board, "H2")
elseif cards[1] == 6
square = findfirst(board, "R1")
elseif cards[1] == 7 || cards[1] == 8
if board[square] == "CH1"
square = findfirst(board, "R2")
elseif board[square] == "CH2"
square = findfirst(board, "R3")
elseif board[square] == "CH3"
square = findfirst(board, "R1")
end
elseif cards[1] == 9
if board[square] == "CH1"
square = findfirst(board, "U1")
elseif board[square] == "CH2"
square = findfirst(board, "U2")
elseif board[square] == "CH3"
square = findfirst(board, "U1")
end
elseif cards[1] == 10
square = (square - 3) % 40 + ((square - 3 % 40 == 0 ? 40 : 0))
end
p = pop!(cards)
unshift!(cards, p)
return square, cards
end
result = zeros(Int, 40)
consec_doubles = 0
square = 1
for i = 1:n
throw_1 = rand(collect(1:dice))
throw_2 = rand(collect(1:dice))
if throw_1 == throw_2
consec_doubles += 1
else
consec_doubles = 0
end
if consec_doubles != 3
move = throw_1 + throw_2
square = (square + move) % 40 +((square + move) % 40 == 0 ? 40 : 0)
if board[square] == "G2J"
square = findfirst(board, "JAIL")
elseif board[square][1:2] == "CC"
square, cc_cards = take_cc_card(square, cc_cards)
elseif board[square][1:2] == "CH"
square, ch_cards = take_ch_card(square, ch_cards)
if board[square][1:2] == "CC"
square, cc_cards = take_cc_card(square, cc_cards)
end
end
else
square = findfirst(board, "JAIL")
consec_doubles = 0
end
if i >= start
result[square] += 1
end
end
result_tuple = Vector{Tuple{Float64, Int}}()
for i = 1:40
percent = result[i] * 100 / sum(result)
push!(result_tuple, (percent, i))
end
sort!(result_tuple, lt = (x, y) -> isless(x[1], y[1]), rev=true)
modal_squares = Vector{String}()
modal_string = ""
modal_percents = Vector{Float64}()
for i = 1:modal
push!(modal_squares, board[result_tuple[i][2]])
push!(modal_percents, result_tuple[i][1])
k = result_tuple[i][2] - 1
modal_string *= (k < 10 ? ("0" * string(k)) : string(k))
end
return modal_string, modal_squares, modal_percents
end

@code_warntype main(1_000_000, 4, 101, 5)

但是,当我通过在第一个参数后面插入分号而不是逗号来将最后三个参数更改为关键字时...

function main(n::Int; dice::Int=6, start::Int=1, modal::Int=3) ::Tuple{String, Vector{String}, Vector{Float64}}

...我似乎遇到了类型稳定性问题。

@code_warntype main(1_000_000, dice=4, start=101, modal=5)

现在,当我运行 @code_warntype 时,我在正文中获得了一个具有 ANY 类型的临时变量和一个 UNION 实例.

奇怪的是,这似乎并没有对性能造成影响,因为在三个基准测试中,“参数”版本的平均运行时间为 431.594 毫秒,而“关键字”版本的运行时间为 413.149 毫秒。不过,我很想知道:

(a) 为什么会发生这种情况;

(b) 作为一般规则,ANY 类型的临时变量的出现是否值得关注;和

(c) 作为一般规则,从性能角度来看,使用关键字而不是普通函数参数是否有任何优势。

最佳答案

这是我对这三个问题的看法。在答案中,我假设 Julia 0.6.3,除非我在帖子末尾明确声明我指的是 Julia 0.7。

(a) 带有 Any 变量的代码是负责处理关键字参数的代码的一部分(例如,确保函数签名允许传递的关键字参数)。原因是关键字参数在函数内以 Vector{Any} 形式接收。该向量保存元组([参数名称], [参数值])。函数所做的实际“工作”发生在使用 Any 变量的这部分之后。

您可以通过比较调用来看到这一点:

@code_warntype main(1_000_000, dice=4, start=101, modal=5)

@code_warntype main(1_000_000)

对于带有关键字参数的函数。第二次调用仅包含上面第一次调用生成的最后一行报告,所有其他调用都负责处理传递的关键字参数。

(b) 作为一般规则,这当然可能是一个问题,但在这种情况下这是无济于事的。具有 Any 的变量保存有关关键字参数名称的信息。

(c) 一般来说,您可以假设位置参数并不比关键字参数慢,但可以更快。这是一个 MWE(实际上,如果您运行 @code_warntype f(a=10),您也会看到这个 Any 变量):

julia> using BenchmarkTools

julia> f(;a::Int=1) = a+1
f (generic function with 1 method)

julia> g(a::Int=1) = a+1
g (generic function with 2 methods)

julia> @benchmark f()
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 1.865 ns (0.00% GC)
median time: 1.866 ns (0.00% GC)
mean time: 1.974 ns (0.00% GC)
maximum time: 14.463 ns (0.00% GC)
--------------
samples: 10000
evals/sample: 1000

julia> @benchmark f(a=10)
BenchmarkTools.Trial:
memory estimate: 96 bytes
allocs estimate: 1
--------------
minimum time: 52.994 ns (0.00% GC)
median time: 54.413 ns (0.00% GC)
mean time: 65.207 ns (10.65% GC)
maximum time: 3.466 μs (94.78% GC)
--------------
samples: 10000
evals/sample: 986

julia> @benchmark g()
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 1.865 ns (0.00% GC)
median time: 1.866 ns (0.00% GC)
mean time: 1.954 ns (0.00% GC)
maximum time: 13.062 ns (0.00% GC)
--------------
samples: 10000
evals/sample: 1000

julia> @benchmark g(10)
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 1.865 ns (0.00% GC)
median time: 1.866 ns (0.00% GC)
mean time: 1.949 ns (0.00% GC)
maximum time: 13.063 ns (0.00% GC)
--------------
samples: 10000
evals/sample: 1000

现在你可以看到,实际上关键字参数的惩罚是在它被传递时(当你在 @code_warntype 中有 Any 变量作为 Julia 时,情况正是如此那么必须做更多的工作)。请注意,惩罚很小,并且在执行很少工作的函数中是可见的。对于进行大量计算的函数,大多数时候可以忽略它。

另外请注意,如果您不指定关键字参数的类型,那么在显式传递关键字参数值时,惩罚会更大,因为 Julia 不会分派(dispatch)关键字参数类型(您也可以运行 @code_warntype 见证这一点):

julia> h(;a=1) = a+1
h (generic function with 1 method)

julia> @benchmark h()
BenchmarkTools.Trial:
memory estimate: 0 bytes
allocs estimate: 0
--------------
minimum time: 1.865 ns (0.00% GC)
median time: 1.866 ns (0.00% GC)
mean time: 1.960 ns (0.00% GC)
maximum time: 13.996 ns (0.00% GC)
--------------
samples: 10000
evals/sample: 1000

julia> @benchmark h(a=10)
BenchmarkTools.Trial:
memory estimate: 96 bytes
allocs estimate: 1
--------------
minimum time: 75.433 ns (0.00% GC)
median time: 77.355 ns (0.00% GC)
mean time: 89.037 ns (7.87% GC)
maximum time: 2.128 μs (89.73% GC)
--------------
samples: 10000
evals/sample: 971

在 Julia 0.7 中,关键字参数作为包含 NamedTuple 的 Base.Iterator.Pairs 接收,因此 Julia 在编译时知道传递参数的类型。这意味着使用关键字参数比 Julia 0.6.3 更快(但同样,您不应该期望它们比位置参数更快)。你可以看到这个购买运行了类似的基准测试(我只是稍微改变了函数的作用,以便为 Julia 编译器提供更多的工作),但在 Julia 0.7 下(你也可以看看 @code_warntype 在这些函数上可以看到类型推断在 Julia 0.7 中效果更好):

julia> using BenchmarkTools

julia> f(;a::Int=1) = [a]
f (generic function with 1 method)

julia> g(a::Int=1) = [a]
g (generic function with 2 methods)

julia> h(;a=1) = [a]
h (generic function with 1 method)

julia> @benchmark f()
BenchmarkTools.Trial:
memory estimate: 96 bytes
allocs estimate: 1
--------------
minimum time: 31.724 ns (0.00% GC)
median time: 34.523 ns (0.00% GC)
mean time: 50.576 ns (22.80% GC)
maximum time: 53.465 μs (99.89% GC)
--------------
samples: 10000
evals/sample: 1000

julia> @benchmark f(a=10)
BenchmarkTools.Trial:
memory estimate: 96 bytes
allocs estimate: 1
--------------
minimum time: 31.724 ns (0.00% GC)
median time: 34.057 ns (0.00% GC)
mean time: 50.739 ns (22.83% GC)
maximum time: 55.303 μs (99.89% GC)
--------------
samples: 10000
evals/sample: 1000

julia> @benchmark g()
BenchmarkTools.Trial:
memory estimate: 96 bytes
allocs estimate: 1
--------------
minimum time: 31.724 ns (0.00% GC)
median time: 34.523 ns (0.00% GC)
mean time: 50.529 ns (22.77% GC)
maximum time: 54.501 μs (99.89% GC)
--------------
samples: 10000
evals/sample: 1000

julia> @benchmark g(10)
BenchmarkTools.Trial:
memory estimate: 96 bytes
allocs estimate: 1
--------------
minimum time: 31.724 ns (0.00% GC)
median time: 34.523 ns (0.00% GC)
mean time: 50.899 ns (23.27% GC)
maximum time: 56.246 μs (99.90% GC)
--------------
samples: 10000
evals/sample: 1000

julia> @benchmark h()
BenchmarkTools.Trial:
memory estimate: 96 bytes
allocs estimate: 1
--------------
minimum time: 31.257 ns (0.00% GC)
median time: 34.057 ns (0.00% GC)
mean time: 50.924 ns (22.87% GC)
maximum time: 55.724 μs (99.88% GC)
--------------
samples: 10000
evals/sample: 1000

julia> @benchmark h(a=10)
BenchmarkTools.Trial:
memory estimate: 96 bytes
allocs estimate: 1
--------------
minimum time: 31.724 ns (0.00% GC)
median time: 34.057 ns (0.00% GC)
mean time: 50.864 ns (22.60% GC)
maximum time: 53.389 μs (99.83% GC)
--------------
samples: 10000
evals/sample: 1000

关于julia - 在 Julia 中将函数参数更改为关键字似乎会引入类型不稳定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51114781/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com