Performance 添加类型声明会显著降低Julia算法的速度

Performance 添加类型声明会显著降低Julia算法的速度,performance,types,julia,julia-studio,Performance,Types,Julia,Julia Studio,我在Julia Studio(Julia 0.2.0,OSX 10.8.2)中编写了一个非常基本的算法,用于计算炉石中给定法力曲线的每回合平均剩余法力。在使用该算法时,我向所有变量添加了类型声明,认为这将有助于提高总体速度。惊喜!添加的类型声明使代码运行速度慢了4倍多(从~7秒到~28秒)。是什么导致了这种奇怪的行为,我该如何修复它?感觉添加类型应该有助于编译器生成更快的代码,或者至少没有什么区别 以下是不带类型声明的代码(运行时6.76s): 函数所有_组合(n) 结果=数组{Int64}[]

我在Julia Studio(Julia 0.2.0,OSX 10.8.2)中编写了一个非常基本的算法,用于计算炉石中给定法力曲线的每回合平均剩余法力。在使用该算法时,我向所有变量添加了类型声明,认为这将有助于提高总体速度。惊喜!添加的类型声明使代码运行速度慢了4倍多(从~7秒到~28秒)。是什么导致了这种奇怪的行为,我该如何修复它?感觉添加类型应该有助于编译器生成更快的代码,或者至少没有什么区别

以下是不带类型声明的代码(运行时6.76s):

函数所有_组合(n)
结果=数组{Int64}[]
对于x in[1:n]
追加(结果,收集(组合(1:n,x)))
结束
返回结果
结束
曲线=[2,3,4,5,5,4,3,2,1,1]
games=数组{Int64}[]
函数execute()
对于[1:5000]中的游戏
deck=mapreduce(
(x) ->填充(x[1],x[2]),
追加
枚举(曲线)
函数drawcard()
卡=拼接!(甲板,兰特(1:长度(甲板)))
结束
hand=[drawcard()表示[1:3]中的n
翻转剩余物=Int64[]
对于[1:10]中的法力值
推(手,抽卡())
可能的_播放=所有_组合(长度(手))
地图!(
玩->地图(我->手[i],玩),
可能的(不适用)
过滤器!(x->sum(x)sum(a)>sum(b)?a:b,
可能的(不适用)
牌局中的牌
捻接(手,findfirst(手,卡片))
结束
推(回合剩余,法力总和(游戏))
其他的
推(翻转剩余物,法力)
结束
结束
推(游戏、转身剩菜)
结束
结束
println(@execute())
println(“平均超过$(长度(游戏))游戏”)
上交[1:长度(游戏[1])]
平均值(地图(游戏->游戏[回合],游戏))
println(“左转$turn:$avrg”)
结束
println(“平均剩余法力:$(平均(减少(vcat,游戏))))
println(“完成”)
下面是带有类型声明的代码(运行时28.48s):

函数所有_组合(n)
结果=数组{Int64}[]
对于x in[1:n]
追加(结果,收集(组合(1:n,x)))
结束
返回结果
结束
曲线::数组{Int64}=[2,3,4,5,5,4,3,2,1]
games=数组{Int64}[]
函数execute()
用于[1:5000]中的游戏\n::Int64
deck::数组{Int64}
deck=mapreduce(
(x) ->填充(x[1],x[2]),
追加
枚举(曲线)
函数drawcard()
卡::Int64=拼接!(甲板,兰特(1:长度(甲板)))
结束
hand::数组{Int64}
hand=[drawcard()表示[1:3]中的n
turn_剩菜::数组{Int64}
翻转剩余物=Int64[]
对于[1:10]中的法力::Int64
推(手,抽卡())
可能的_播放::数组{Array{Int64}}=所有_组合(长度(手))
地图!(
play->map(i::Int64->hand[i],play),
可能的(不适用)
过滤器!(x::数组{Int64}->sum(x)sum(a)>sum(b)?a:b,
可能的(不适用)
用于播放中的卡::Int64
捻接(手,findfirst(手,卡片))
结束
推(回合剩余,法力总和(游戏))
其他的
推(翻转剩余物,法力)
结束
结束
推(游戏、转身剩菜)
结束
结束
println(@execute())
println(“平均超过$(长度(游戏))游戏”)
上交[1:长度(游戏[1])]
平均值(地图(游戏->游戏[回合],游戏))
println(“左转$turn:$avrg”)
结束
println(“平均剩余法力:$(平均(减少(vcat,游戏))))
println(“完成”)

值得注意的是,即使是最快的版本也比用JavaScript编写的等效代码慢一点。不过,这可能只是因为糟糕的实现。我毫不怀疑,一个更好的算法会在一周中的任何一天胜过JS。

一个放缓的原因是:你使用了很多匿名函数和高阶函数,比如


地图!(
play->map(i::Int64->hand[i],play),
可能的游戏
)

过滤器!(x::Array{Int64}->sum(x)第一件事:Julia猜测你什么都不说,坚持说可以。
Array{Int64}
是一个不完整的数组声明,可以容纳任何N维数组。
Array{Int,1}
是你真正想要的1d向量。你是对的,将类型更改为Array{Int,1}将速度提高到5秒左右。谢谢!如果你想要更快的增益,你可以声明曲线和游戏常数
function all_combinations(n)
    result = Array{Int64}[]
    for x in [1:n]
        append!(result, collect(combinations(1:n,x)))
    end
    return result
end

curve = [2, 3, 4, 5, 5, 4, 3, 2, 1, 1]

games = Array{Int64}[]

function execute()
    for game_n in [1:5000]

        deck = mapreduce(
            (x) -> fill(x[1], x[2]),
            append!,
            enumerate(curve))

        function drawcard()
            card = splice!(deck, rand(1:length(deck)))
        end

        hand = [drawcard() for n in [1:3]]

        turn_leftovers = Int64[]

        for mana in [1:10]

            push!(hand, drawcard())

            possible_plays = all_combinations(length(hand))
            map!(
                play -> map(i -> hand[i], play),
                possible_plays)
            filter!(x -> sum(x) <= mana, possible_plays)

            if  !isempty(possible_plays)

                play = reduce(
                    (a, b) -> sum(a) > sum(b) ? a : b,
                    possible_plays)
                for card in play
                    splice!(hand, findfirst(hand, card))
                end
                push!(turn_leftovers, mana - sum(play))
            else
                push!(turn_leftovers, mana)
            end

        end

        push!(games, turn_leftovers)

    end
end

println(@elapsed execute())

println("Averaging over $(length(games)) games")
for turn in [1:length(games[1])]
    avrg = mean(map(game -> game[turn], games))
    println("Left on turn $turn: $avrg")
end
println("Average mana leftover: $(mean(reduce(vcat, games)))")
println("Done")
function all_combinations(n)
    result = Array{Int64}[]
    for x in [1:n]
        append!(result, collect(combinations(1:n,x)))
    end
    return result
end

curve::Array{Int64} = [2, 3, 4, 5, 5, 4, 3, 2, 1, 1]

games = Array{Int64}[]

function execute()
    for game_n::Int64 in [1:5000]

        deck::Array{Int64}
        deck = mapreduce(
            (x) -> fill(x[1], x[2]),
            append!,
            enumerate(curve))

        function drawcard()
            card::Int64 = splice!(deck, rand(1:length(deck)))
        end

        hand::Array{Int64}
        hand = [drawcard() for n in [1:3]]

        turn_leftovers::Array{Int64}
        turn_leftovers = Int64[]

        for mana::Int64 in [1:10]

            push!(hand, drawcard())

            possible_plays::Array{Array{Int64}} = all_combinations(length(hand))
            map!(
                play -> map(i::Int64 -> hand[i], play),
                possible_plays)
            filter!(x::Array{Int64} -> sum(x) <= mana, possible_plays)

            if  !isempty(possible_plays)

                play::Array{Int64} = reduce(
                    (a::Array{Int64}, b::Array{Int64}) -> sum(a) > sum(b) ? a : b,
                    possible_plays)
                for card::Int64 in play
                    splice!(hand, findfirst(hand, card))
                end
                push!(turn_leftovers, mana - sum(play))
            else
                push!(turn_leftovers, mana)
            end

        end

        push!(games, turn_leftovers)

    end
end

println(@elapsed execute())

println("Averaging over $(length(games)) games")
for turn in [1:length(games[1])]
    avrg = mean(map(game -> game[turn], games))
    println("Left on turn $turn: $avrg")
end
println("Average mana leftover: $(mean(reduce(vcat, games)))")
println("Done")