我用numpy测试了这个talk[pytables]中演示的一个例子(第20/57页)。在
结果表明,a[:,1].sum()
只需9.3ms,而{
我试图复制它,但没有成功。我量错了吗?或者从2010年开始,纽比的情况发生了变化?在
$ python2 -m timeit -n1000 --setup \
'import numpy as np; a = np.random.randn(4000,4000);' 'a[:,1].sum()'
1000 loops, best of 3: 16.5 usec per loop
$ python2 -m timeit -n1000 --setup \
'import numpy as np; a = np.random.randn(4000,4000);' 'a[1,:].sum()'
1000 loops, best of 3: 13.8 usec per loop
$ python2 --version
Python 2.7.7
$ python2 -c 'import numpy; print numpy.version.version'
1.8.1
虽然我可以衡量第二个版本的好处(因为numpy使用C风格的行排序,所以应该更少的缓存未命中),但我看不出pytables贡献者所说的那种巨大差异。在
另外,在使用列V行求和时,似乎看不到更多的缓存未命中。在
编辑
到目前为止,我的洞察力是我用错了timeit
模块。使用同一个数组(或数组的行/列)重复运行几乎肯定会被缓存(我有一级数据缓存的32KiB
,因此其中有一行很适合:4000 * 4 byte = 15k < 32k
)。
使用@alim的answer中的脚本和一个单循环(nloop=1
)和十次试验nrep=10
,并改变我测量的随机数组(n x n
)的大小
*n=10k
及更高版本不再适合L1d缓存。
我仍然不确定是否能找到原因,因为perf
显示了与更快的行和相同的缓存未命中率(有时甚至更高)。在
Perf
数据:nloop = 2
和nrep=2
,所以我希望一些数据仍在缓存中。。。第二轮。在
n=10k
perf stat -B -e cache-references,cache-misses,L1-dcache-loads,L1-dcache-load-misses,L1-dcache-stores,L1-dcache-store-misses,L1-dcache-prefetches,cycles,instructions,branches,faults,migrations ./answer1.py 2>&1 | sed 's/^/ /g'
row sum: 103.593 us
Performance counter stats for './answer1.py':
25850670 cache-references [30.04%]
1321945 cache-misses # 5.114 % of all cache refs [20.04%]
5706371393 L1-dcache-loads [20.00%]
11733777 L1-dcache-load-misses # 0.21% of all L1-dcache hits [19.97%]
2401264190 L1-dcache-stores [20.04%]
131964213 L1-dcache-store-misses [20.03%]
2007640 L1-dcache-prefetches [20.04%]
21894150686 cycles [20.02%]
24582770606 instructions # 1.12 insns per cycle [30.06%]
3534308182 branches [30.01%]
3767 faults
6 migrations
7.331092823 seconds time elapsed
n=10k
perf stat -B -e cache-references,cache-misses,L1-dcache-loads,L1-dcache-load-misses,L1-dcache-stores,L1-dcache-store-misses,L1-dcache-prefetches,cycles,instructions,branches,faults,migrations ./answer1.py 2>&1 | sed 's/^/ /g'
column sum: 377.059 us
Performance counter stats for './answer1.py':
26673628 cache-references [30.02%]
1409989 cache-misses # 5.286 % of all cache refs [20.07%]
5676222625 L1-dcache-loads [20.06%]
11050999 L1-dcache-load-misses # 0.19% of all L1-dcache hits [19.99%]
2405281776 L1-dcache-stores [20.01%]
126425747 L1-dcache-store-misses [20.02%]
2128076 L1-dcache-prefetches [20.04%]
21876671763 cycles [20.00%]
24607897857 instructions # 1.12 insns per cycle [30.00%]
3536753654 branches [29.98%]
3763 faults
9 migrations
7.327833360 seconds time elapsed
编辑2
我想我已经了解了一些方面,但是这个问题我想还没有得到回答。目前,我认为这个求和示例根本没有揭示任何关于CPU缓存的信息。为了消除numpy/python的不确定性,我尝试在C中使用perf
进行求和,结果如下所示。在
有趣。我可以重现塞巴斯蒂安的表演:
但是,如果我尝试使用更大的阵列:
^{pr2}$但是,如果我再试一次:
所以,不确定这是怎么回事,但这种抖动可能是由于缓存效应。也许新的体系结构在预测模式访问方面更明智了,因此可以更好地进行预取?在
无论如何,为了便于比较,我使用的是Numpy1.8.1、LinuxUbuntu14.04和一台i5-3380mCPU@2.90GHz的笔记本电脑。在
编辑:在考虑过这一点之后,是的,我会说第一次执行sum时,列(或行)是从RAM中获取的,但是第二次操作运行时,数据在缓存中(对于行和列两种版本),所以它执行的速度很快。由于时间花费最少的运行,这就是为什么我们看不到时间上的巨大差异。在
另一个问题是为什么我们有时会看到差异(使用timeit)。但是缓存是一种奇怪的野兽,尤其是在一次执行多个进程的多核机器中。在
我看不出你的复制尝试有什么错,但请记住,这些幻灯片都是2010年的,从那时起numpy已经发生了很大的变化。根据dates of numpy releases,我猜弗朗西斯可能在用v1.5。在
使用此脚本对第v行列和进行基准测试:
我检测到numpy v1.5的列和速度下降了大约50%:
^{pr2}$相比之下,v1.8.1版本的速度下降了30%,您使用的是:
有趣的是,在最近的numpy版本中,这两种类型的缩减实际上都有点慢。我必须深入研究numpy的源代码 去理解为什么会这样。在
更新
我已经相应地更新了我的脚本和上面的结果
我们还可以通过为每个调用创建一个全新的随机数组来消除跨调用缓存的任何效果(临时局部性)——只需将
nloop
设置为1,将nrep
设置为一个相当小的数字(除非您真的很喜欢看油漆干燥),比如10。在在4000x4000阵列上的
nloop=1
,nreps=10
:这有点像,但我仍然无法真正复制弗朗西斯的幻灯片所显示的巨大效果。也许这并不令人惊讶,但是-效果可能非常依赖于编译器、体系结构和/或内核。在
我在C中编写了求和示例:结果显示为CPU time度量,我总是使用
gcc -O1 using-c.c
来编译(gcc版本:gcc版本4.9.0 20140604)。源代码如下。在我选择矩阵大小为
n x n
。对于n<2k
,行和列的总和没有任何可测量的差异(对于n=2k
,每次运行6-7 us)。在行总和
e、 gn=20k
^{pr2}$列
例如
n=20k
讨论
行总和更快。我并没有从任何缓存中获益,也就是说,重复求和并不比初始求和快多少。列求和的速度要慢得多,但在5-8次迭代中它会稳步增加。在}之间,这种增长最为明显,其中缓存有助于将速度提高约10倍。在较大的阵列中,加速仅为因子2。我还观察到,虽然行和收敛非常快(经过一次或两次试验),列求和收敛需要更多的迭代(5次或更多)。在
n=4k
到{给我上一课:
n>30k
很明显,对于列求和,它已经在n>2k
处变得明显。在使用
perf
,我看不出有什么大的区别。但是C程序的大部分工作是用随机数据填充数组。我不知道如何消除这些“设置”数据。。。在以下是本例的C代码:
相关问题 更多 >
编程相关推荐