当使用更多线程时,使用prange的Numba并行化速度较慢



我尝试了一个简单的代码,用numba和prange并行化一个循环。但是由于某种原因,当我使用更多的线程而不是更快时,它会变慢。为什么会发生这种情况?(cpu ryzen 7 2700x 8核16线程3.7GHz)

from numba import njit, prange,set_num_threads,get_num_threads
@njit(parallel=True,fastmath=True)
def test1():
x=np.empty((10,10))
for i in prange(10):
for j in range(10):
x[i,j]=i+j
Number of threads : 1
897 ns ± 18.3 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 2
1.68 µs ± 262 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 3
2.4 µs ± 163 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 4
4.12 µs ± 294 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 5
4.62 µs ± 283 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 6
5.01 µs ± 145 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 7
5.52 µs ± 194 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 8
4.85 µs ± 140 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 9
6.47 µs ± 348 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 10
6.88 µs ± 120 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 11
7.1 µs ± 154 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 12
7.47 µs ± 159 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 13
7.91 µs ± 160 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 14
9.04 µs ± 472 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 15
9.74 µs ± 581 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)
Number of threads : 16
11 µs ± 967 ns per loop (mean ± std. dev. of 10 runs, 100000 loops each)

这完全正常。Numba需要创建线程和之间分配工作所以它们可以并行地执行计算。Numba可以使用不同的线程后端。默认的OpenMP和默认的OpenMP实现应该是IOMP (ICC/Clang的OpenMP运行时),它试图只创建一次线程。尽管如此,在线程之间共享工作比迭代100多个值要慢得多。。现代主流处理器应该能够在不到0.1-0.2秒的时间内依次执行两个嵌套循环。Numba还应该能够展开两个循环。Numba函数的开销通常也只有几百纳秒。Numpy数组的分配应该比实际循环慢得多。此外,还有其他开销导致该代码在多线程情况下明显变慢,即使前面的开销可以忽略不计。例如false-sharing导致写操作大部分是序列化的,因此比在一个唯一线程中完成写操作要慢(因为在x86-64平台上的LLC上运行的缓存线反弹效应)。

请注意,创建一个线程的时间通常远远超过1个小时,因为需要一个系统调用。

简而言之:在任务足够大并且可以有效并行时使用线程.

最新更新