Julia 没有使用所有可用的 CPU 线程。

5
我在使用Julia进行计算时,发现它比numpy慢得多(大约25倍)。后来我意识到,在我的PC上,Julia只使用了8个线程中的全部96个CPU线程(48个物理核心),而numpy似乎没有问题地利用了超过70个线程。
即使使用 $julia --thread 96参数运行Julia也没有任何区别,尽管 julia> Threads.nthreads() 返回值是96
此外,让我有点失望的是,我怀疑Julia即使使用全部的96个线程,仍然可能无法与numpy的速度匹敌。
这是Julia的代码。我只是用julia> @time calc_fid(mat_a, mat_b)测量时间,平均需要90秒。
using Statistics
using LinearAlgebra

function calc(A::Array{Float32,2}, B::Array{Float32,2})
    μ_A = mean(A, dims=2)
    μ_B = mean(B, dims=2)
    σ_A = cov(A, dims=2)
    σ_B = cov(B, dims=2)

    ssdiff = sum((μ_A - μ_B).^2)
    covmean = sqrt(σ_A * σ_B)
    res = ssdiff + tr(σ_A .+ σ_B .- 2.0 * covmean)
    
    return res
end

这里是使用numpy编写的代码,平均需要3.5秒才能运行。 使用time.perf_counter()进行测量。

import numpy as np

from numpy import cov
from numpy import trace
from scipy.linalg import sqrtm

def calc(A, B):
    mu_A = A.mean(axis=0)
    mu_B = B.mean(axis=0)
    sigma_A = cov(A, rowvar=False)
    sigma_B = cov(B, rowvar=False)

    ssdiff = np.sum((mu_A - mu_B) ** 2.0)
    covmean = sqrtm(sigma_A.dot(sigma_B))
    res = ssdiff + trace(sigma_A + sigma_B - 2.0 * covmean)

    return res

任何建议/解释都将不胜感激!

1个回答

6

你的Julia示例代码实际上没有使用Julia的多线程功能。

在Julia中,如果想要使用多线程,通常需要显式地启用它。像meancov这样的函数不会自动地通过启动具有多个线程的Julia而变成多线程函数;如果你想让这些函数变成多线程函数,你需要编写多线程版本的这些函数,或者使用已经编写好的包。

在你的Julia示例代码中,唯一会多线程的是线性代数部分,因为它会回退到BLAS(与numpy一样),它拥有完全独立于Julia多线程的自己的多线程系统(BLAS是用Fortran编写的)。但是,由于默认情况下这也很可能只使用了8个线程,所以它也很可能没有使用所有的96个线程。可以使用以下命令检查Julia中BLAS所使用的线程数:

using LinearAlgebra
BLAS.get_num_threads()

或类似地设置为

BLAS.set_num_threads(n)

感谢您的解释!设置julia> LinearAlgebra.BLAS.set_num_threads(96)后,代码现在使用了大约一半的线程或者说是双CPU系统中的一个CPU。但我观察到代码在BLAS例程之外只使用了一个线程,时间缩短到了90至73秒。 - Kim
是的,这很有道理。如果你想要在Julia中高效地进行多线程处理,可以尝试查看Folds生态系统,或者LoopVectorization的线程版本(或者普通的Threads接口)。 - cbk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接