Multithreading 存储多线程函数调用输出的最佳方法

Multithreading 存储多线程函数调用输出的最佳方法,multithreading,julia,Multithreading,Julia,我有一个函数f(),它返回一个数据帧,数据帧的行数我事先不知道。我正在多线程上下文中调用f()。我将结果存储如下: results = [DataFrame() for _ in 1:100] Threads.@threads for hi in 1:100 results[hi] = f(df) end 当我运行这段代码时,内存使用量会增加,大概是因为results在获得数据帧的大小时必须不断调整自身大小[编辑:这不是真的]。预分配结果数组以避免内存膨胀的最佳方法是什么 ****使

我有一个函数
f()
,它返回一个数据帧,数据帧的行数我事先不知道。我正在多线程上下文中调用
f()
。我将结果存储如下:

results = [DataFrame() for _ in 1:100]

Threads.@threads for hi in 1:100
    results[hi] = f(df)
end
当我运行这段代码时,内存使用量会增加,大概是因为
results
在获得数据帧的大小时必须不断调整自身大小[编辑:这不是真的]。预分配结果数组以避免内存膨胀的最佳方法是什么

****使用MWE更新****

function func(df::DataFrame)
    X = df[:time]
    indices = findall(X .> 0)
end

# read in R data
rds = "blablab.rds"
objs = load(rds);

params = collect(0.5:0.005:0.7);

for i in 1:length(objs)
    cols = [string(name) for name in names(objs.data[i]) if occursin("blabla",string(name))]
    hypers = [(a,b) for a in cols, b in params]

    results = [DataFrame() for _ in 1:length(hypers)]

    # HERE IS WHERE THE MEMORY BLOWS UP
    Threads.@threads for hi in 1:length(hypers)
        name, val = hypers[hi]
        results[hi] = func(objs.data[i])
    end
end

df
为0.7GB。当我运行这段代码时,我的内存使用率会上升到~30GB!!!似乎仅仅访问
func()
中的
df
列就是在复制整个内容?

请在下面找到相同代码的两个版本-单线程和多线程从
f()
函数返回的一组
DataFrame
生成一个
DataFrame
,并且具有随机长度

using Random
using DataFrames
using BenchmarkTools

function f(rngs::Vector{Random.MersenneTwister}, offset)::DataFrame
    t = Threads.threadid()
    n = rand(rngs[t+offset], 1:20)
    DataFrame(a=1:n,b=21:(20+n),t=t+offset)
end

function test_threads(rngs::Vector{Random.MersenneTwister})
    res = DataFrame([Int,Int,Int],[:a,:b,:t],0)
    lock = Threads.SpinLock()
    Threads.@threads for i in 1:100
        df = f(rngs,0)
        Threads.lock(lock)
        append!(res,df)
        Threads.unlock(lock)
    end
    res
end

function test_normal(rngs::Vector{Random.MersenneTwister})    
    res = DataFrame([Int,Int,Int],[:a,:b,:t],0)    
    for i in 1:100
        append!(res,f(rngs, i%2))
    end
    res
end
现在让我们进行测试:

julia> rngs = [Random.MersenneTwister(i) for i in 1:2];

julia> @btime test_normal($rngs);

  891.306 μs (5983 allocations: 476.67 KiB)

rngs = [Random.MersenneTwister(i) for i in 1:Threads.nthreads()];

@btime test_threads($rngs);

  674.559 μs (5549 allocations: 425.69 KiB)

结果
数组不必不断调整自身大小。事实上,我认为它在循环过程中不会调整大小。你把这个循环放在函数里面了吗?您能否为
f()
共享一个MWE,以便我们更清楚地看到导致高内存使用率的问题?顺便说一句,所有的
results
hold都是指向
DataFrame
s的指针。F()太长了,无法发布,但我已经将问题缩小到保存F()结果的位置。如果我只调用f(),但不将结果保存到列表中,那么就没有问题了。我不认为问题与您问题中的代码片段有关。如果未保存
f()
的结果,垃圾收集器可以安全地回收它,因此内存使用不会增加。因此,它没有说明问题在哪里。也许,你真的没有什么问题。也许你的
DataFrame
s真的很大。您可以使用
Base.summarysize(df)
检查一个
数据帧的大小。创建一个新的minimal
f()
可能有助于快速识别问题。您是对的。我发布了一个MWE。
func
在更新后的MWE中返回一个
数组
索引,假设您的体系结构是64位的,每个索引都是
Int64
。每个
Int64
占用内存8字节。您将在每次迭代后存储这些数组。因此,记忆必须增长。此增长量将取决于每个
数据帧
的行数,其中
时间
大于0。访问
数据帧
列不会分配内存。访问
DataFrame
不是这里的问题。您必须更改代码以减少内存使用。可能删除
findall
,只需将结果存储为
位数组
,即只需使用(X.>0)…谢谢。这很有效。我现在明白了问题不在于我存储结果的方式。内存问题发生在每个线程调用的函数内部的某个地方。是否每次都复制dataframe参数?在我的代码中,每次都创建一个新的数据帧。但是,
append与Gnu相反,R只是将元素添加到
数据帧
中,而不是重新创建整个新的
数据帧