Julia 1.5.2 性能问题

Julia 1.5.2 Performance Questions

我目前正在尝试实现元启发式(遗传)算法。在这次冒险中,我还想尝试创建一些快速高效的代码。但是,我在创建高效编码方面的经验并不是很好。因此,我想知道是否有人可以提供一些“快速提示”来提高我的代码效率。我已经为我的代码创建了一个小的功能示例,其中包含代码将包含的大部分元素,这些元素与预分配数组、自定义可变结构、随机数、推入数组等有关。

我已经尝试探索的选项是关于包“StaticArrays”的选项。然而,我的许多数组必须是可变的(因此我们需要 MArrays)并且其中许多将变得非常大 > 100。StaticArrays 的文档指定 StaticArrays 包的大小必须保持较小以保持高效。

根据文档,关于 rand(),Julia 1.5.2 应该是线程安全的。因此,我尝试在我的函数中使用多线程 for 循环,以使它们 运行 更快。这会导致性能略有提高。

但是,如果人们能找到一种更有效的方法来分配数组或将 SpotPrices 推入数组,将不胜感激!也非常欢迎任何其他性能提示!

# Packages
clearconsole()
using DataFrames
using Random
using BenchmarkTools
Random.seed!(42)

df = DataFrame( SpotPrice = convert(Array{Float64}, rand(-266:500,8832)),
month = repeat([1,2,3,4,5,6,7,8,9,10,11,12]; outer = 736),
hour = repeat([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]; outer = 368))

# Data structure for the prices per hour
mutable struct SpotPrices
    hour :: Array{Float64,1}
end

# Fill-out data structure
function setup_prices(df::DataFrame)
    prices = []
    for i in 1:length(unique(df[:,3]))
        push!(prices, SpotPrices(filter(row -> row.hour == i, df).SpotPrice))
    end
    return prices
end

prices = setup_prices(df)

# Sampler function
function MC_Sampler(prices::Vector{Any}, sample_size::Int64)
    # Picking the samples
    tmp = zeros(sample_size, 24)

    # Sampling per hour
    for i in 1:24
        tmp[:,i] = rand(prices[i].hour, sample_size)
    end
    return tmp
end

samples = MC_Sampler(prices, 100)

@btime setup_prices(df)
@btime MC_Sampler(prices,100)

function setup_prices_par(df::DataFrame)
    prices = []
    @sync Threads.@threads for i in 1:length(unique(df[:,3]))
        push!(prices, SpotPrices(filter(row -> row.hour == i, df).SpotPrice))
    end
    return prices
end


# Sampler function
function MC_Sampler_par(prices::Vector{Any}, sample_size::Int64)
    # Picking the samples
    tmp = zeros(sample_size, 24)

    # Sampling per hour
    @sync Threads.@threads for i in 1:24
         tmp[:,i] = rand(prices[i].hour, sample_size)
    end
    return tmp
end

@btime setup_prices_par(df)
@btime MC_Sampler_par(prices,100)

看看仔细阅读https://docs.julialang.org/en/v1/manual/performance-tips/

基本清理开始于:

  1. 你的 SpotPrices struct 不需要对我可变。无论如何,因为只有一个字段,您可以将其定义为 SpotPrices=Vector{Float64}
  2. 您不想要无类型的容器 - 而不是 prices = []prices = Float64[]
  3. 使用 DataFrames.groupby 比查找唯一元素并按它们过滤要快得多
  4. 如果你不需要初始化而不是不初始化 Vector{Float64}(undef, sample_size)zeros(sample_size, 24)
  5. 快得多
  6. 多线程循环前不需要同步@sync
  7. 创建随机状态 - 每个线程一个单独的状态,并在调用 rand 函数时使用它们