我使用串行和OpenMP实现编写了一个基本的三循环矩阵乘法。对于相同大小(3200x3200),perf stat -a -e instructions,cycles显示:
串行
265,755,992,060 instructions # 0.71 insn per cycle
375,319,584,656 cycles
85.923380841 seconds time elapsed并行(16个线程)
264,524,937,733 instructions # 0.30 insn per cycle
883,342,095,910 cycles
13.381343295 seconds time elapsed在并行运行中,我希望循环的数量与串行运行大致相同。但事实并非如此。
有什么不同之处吗?
更新:
我用8和16个线程重新运行实验,因为处理器最多有16个线程。
Using 8 threads
Max nthread = 16
Total execution Time in seconds: 13.4407235400
MM execution Time in seconds: 13.3349801241
Performance counter stats for 'system wide':
906.51 Joules power/energy-pkg/
264,995,810,457 instructions # 0.59 insn per cycle
449,772,039,792 cycles
13.469242993 seconds time elapsed和
Using 16 threads
Max nthread = 16
Total execution Time in seconds: 13.2618084711
MM execution Time in seconds: 13.1565077840
Performance counter stats for 'system wide':
1,000.39 Joules power/energy-pkg/
264,309,881,365 instructions # 0.30 insn per cycle
882,881,366,456 cycles
13.289234564 seconds time elapsed正如你所看到的,墙上的时钟大致相同,但是16个线程的周期是8个线程的2倍。这意味着,随着更高的周期计数和较低的IPC,这是有可能保持与以前的墙上时钟与更多的线程。根据perf list的说法,这个事件是cpu-cycles OR cycles [Hardware event]。我想知道,一个核还是聚合N个核的平均周期?对此有什么评论吗?
发布于 2022-03-05 10:25:45
假设您的流程完全可伸缩,那么指令的数量将在所有内核之间共享,指令的总数也将相同。同样的情况也适用于循环的数量。但是,当您的过程不缩放时,指令的数量应该是相同的,但是循环的数量会增加。这通常是由于共享资源的争用导致了管道中的阻塞周期。
在并行实现中,内存层次结构没有得到有效的使用,因为并行实现会导致大量缓存丢失,在使用多个线程时可能会使L3或RAM饱和,因此内存暂停。如果使用同步多线程(也称为超线程),这也会导致这个问题,因为同一核心上的两个线程通常不会真正并行运行(内核的某些部分在线程之间共享)。
发布于 2022-03-05 10:24:07
不断的指令是有意义的;不管这些指令是否都运行在同一个核心上,总工作量也是一样的。
您正在使用SMT (例如超线程)吗?当相同的物理核将其时间划分为两个逻辑核时,IPC就会下降。对于某些程序扩展,SMT提高了总体吞吐量;对于缓存绑定程序(如天真的matmul),有两个线程竞争相同的L1/L2缓存伤害。
否则,如果这是16个物理内核,您可能会看到L3争用的效果,而单个线程本身并不拥有所有的L3。
无论如何,由于适当的缓存阻塞,SMT通常会损害matmul /稠密线性代数的总体吞吐量。一个物理内核可以通过一个具有良好调优代码的线程来饱和ALU工作,因此对每个内核缓存的争用只会造成伤害。(但多线程肯定会帮助整个时间,就像你的情况一样。)缓存阻塞的matmul通常是5个嵌套循环,如关于内存,每个程序员都应该知道些什么?末尾的示例所示。
阿格纳·福格(https://agner.org/optimize/)在他的微弓PDF中也提到了超线程对一些工作负载的伤害。
发布于 2022-03-05 14:57:20
矩阵-矩阵乘法是一个典型的运算,理论上有大量的缓存重用,因此可以运行接近峰值速度。除了标准的三循环实现之外,高效使用缓存的优化实现实际上有六个层次:三个循环中的每一个都被平铺,您需要交换循环并正确设置倾斜。这不是小事一桩。
因此,您的实现基本上不使用缓存。也许您可以从L3缓存中得到一些效果,但是,如果有足够的问题大小,肯定不是L1,也可能不是L2。换句话说:你是带宽受限的。问题是,你可能有16个核心,但你没有足够的带宽来满足所有这些。
我必须说,你的6-7的因素有点令人失望,但也许你的架构没有足够的带宽。我知道,对于顶级节点,我期望的是12,但为什么不测试一下呢?编写一个基准测试,它只对内存中的数据进行流处理。矢量-矢量加法。然后看看你能得到多少个线速度。
为了回应你在答覆中提出的几点:
i,j,k更新中,您没有说明哪个索引在内部循环中。无论如何,让编译器生成一个优化报告。您会发现编译器是那么聪明,并且很可能通过交换循环来实现矢量化。真的:你的表现是带宽有限的。但是你只是在测量加速比,所以你并没有真正看到这一点,除了使用你所有的核心将饱和你的带宽和限制你的加速比。
https://stackoverflow.com/questions/71361046
复制相似问题