如何获得多线程dot()函数?

时间:2015-04-01 18:32:07

标签: multithreading julia

当我在具有8个逻辑CPU内核的64位Windows 8.1 PC上的Julia v0.3.7的REPL中执行此操作时:

blas_set_num_threads(CPU_CORES)
const v=ones(Float64,100000)
@time for k=1:1000000;s=dot(v,v);end

我在任务管理器或Process Explorer的CPU表中观察到只使用了12.5%的CPU(1个逻辑CPU核心)。在Windows 7和Windows 8.1上,我也对Julia v0.3.5也有同样的看法。我也通过在命令行上启动“Julia -p 8”来观察相同的行为。返回运行没有“-p 8”命令行选项的Julia REPL,我尝试了这个测试:

blas_set_num_threads(CPU_CORES)
@time peakflops(10000)

在这种情况下,CPU仪表显示100%的CPU使用率。

由于dot()peakflops()都使用BLAS(在我的情况下是OpenBLAS),我希望两者都使用blas_set_num_threads()指定的线程数。但是,只有后一种功能才能实现。 dot()的行为是否由于错误而导致,可能是在OpenBLAS中?

我试图通过使用矩阵乘法函数来解决Julia的缺点。但是,我正在对GB大小的2D数组的子向量上的dot()操作进行迭代,其中子向量使用连续的内存。矩阵乘法迫使我转置每个向量,从而创建一个副本。这在内循环中是昂贵的。因此,我的选择似乎要么学习如何使用Julia的并行处理命令/宏,要么回到Python(英特尔的MKL BLAS按照ddot()的预期运行)。由于dot()是在我试图编写的例程中消耗99%CPU的函数,我希望OpenBLAS能在Julia中为我提供用户友好的最佳解决方案,而不必担心所有的并行处理引擎盖下的复杂性。但也许它不是那么糟糕......

我可以使用一些创建多线程dot()函数。一些示例代码将是最好的帮助。当所有线程在同一台机器上运行时,是否需要使用SharedArray?如果是这样,从Array转换为SharedArray会创建副本吗?由于我的数组非常大,我不希望同时在内存中有两个副本。因为我的向量大约是100,000个,并且来自一个数组的向量以不可预测的顺序使用,对我来说最好的多线程解决方案是dot()函数,它将任务分配到可用的核心中,并对结果进行求和。每个核心。如何在Julia中像BLAS一样有效地做到这一点?

我在这里看到Tim Holy的回答: BLAS v. parallel updates for Julia SharedArray objects 但是,他的例子(我认为)在一个核心上执行整个dot()函数,并且它不回答我的其他问题。

编辑1: 我尝试使用“-p 8”命令行选项运行Julia,并使用dot()替换上面示例中的innersimd()http://docs.julialang.org/en/release-0.3/manual/performance-tips/ 仍然只使用1个核心。我修改了innersimd()以将其参数键入::Array{Float64, 1}然后::SharedArray{Float64, 1},但这仍然使用了1个核心。 :(

编辑2: 我测试了Julia的矩阵乘法(BLAS'gemm!()函数):

blas_set_num_threads(CPU_CORES)
const A=ones(Float64,(4,100000))
const B=ones(Float64,(100000,4))
@time for k=1:100000;s=A*B;end

即使Julia没有使用“-p”命令行选项,Julia也只使用了一个核心。

编辑3: 以下是Python的可比测试代码:

import numpy as np
from scipy.linalg.blas import ddot
from timeit import default_timer as timer
v = np.ones(100000)
start = timer()
for k in range(1000000):
    s = ddot(v,v)
exec_time=(timer() - start)
print
print("Execution took", str(round(exec_time, 3)), "seconds")

我在64位Anaconda3 v2.1.0和WinPython上得到了相同的结果:7.5秒。与我的问题中的第一个例子相比,使用带有OpenBLAS的Julia 0.3.7,在28秒内执行。这使得Python比Julia快4倍,这可以通过OpenBLAS的ddot()单线程实现来解释。

编辑4: 我在Python中进行了一些测试,在我的内循环(N = 100000)中有效地执行了(4xN)*(Nx2)矩阵乘法,我发现它的速度慢了两倍多。我怀疑这是因为缓存需求现在大8倍,因此缓存命中率更差。为了保持Julia中的缓存需求与Python相同,Julia的问题是:如何将我的100000长度向量分解为4个块并且并行执行OpenBLAS的单线程ddot()(并对结果求和)?长度可能不是4的倍数。由于OpenBLAS是单线程的,我可以在同一会话中使用“-p 8”命令行参数和多线程其他自定义函数运行Julia。

编辑5: 使用“-p 8”命令行参数运行Julia v0.3.7,我观察到OpenBLAS gemm!()函数 以多线程运行(对于某些矩阵维度):

blas_set_num_threads(CPU_CORES)
const a = rand(10000, 10000)
@time a * a

1 个答案:

答案 0 :(得分:5)

原因是OpenBLAS'ddot似乎不是多线程的。我认为他们主要为xgemm这样的BLAS-3函数实现多线程,所以如果可能的话,最好的办法是用矩阵乘法来编写你的问题。

如果你能举例说明你如何尝试使用矩阵乘法,那将会有所帮助。也许可以避免换位。你是怎么用Python做的?这一切都只是BLAS,所以Julia应该能够像Python一样做。

使用-p 8标志运行Julia会启动九个Julia副本并关闭BLAS中的多线程,因此可能会使情况更糟糕。

编辑1: 我在单线程和多线程模式下尝试使用OpenBLAS和MKL,如果有任何差异,单线程更快。我不确定OpenBLAS是否会为您的示例使用多个线程。可能是因为多线程加速对于瘦问题很难实现,因此它们只会针对更胖的问题启用多线程。如果在同一台机器上运行Python的示例明显更快?如果是这样,请提供Python示例,以便我可以与我的计算机上的示例进行比较。

编辑2: 我可以确认MKL中ddot的加速。确切地说,编辑1中的评论约为dgemm。积极工作使Julia成为多线程,当发生这种情况时,线程Julia实现可能比OpenBLAS'ddot更快。您现在的选择是

之一
  1. 如果可能,重写您的问题,使OpenBLAS'dgemm使用多个线程,即使用更胖的矩阵。在我的机器上,多线程似乎开始n=16。如果这是可能的,我认为它不会比MKL慢。通常,OpenBLAS'dgemm与MKL的比较好。

  2. 要求OpenBLAS开发人员进行ddot多线程。如果你能为项目提供一个版本会更好,但是高性能的BLAS代码很难写。

  3. 购买MKL并开始销售 Revolution Julia ,包括MKL以支付许可费用。

  4. 编辑3:比较全部是在Julia的Ubuntu机器上进行的,具有Nahalem架构和80个核心(但我最多只使用了16个)。我使用了Julia相同开发版本的两个不同版本:一个与源自OpenBLAS构建链接,一个与MKL链接。我还在最近的Haswell系统上尝试了使用OpenBLAS的Julia,以确保在该架构上ddot没有实现线程化。