我有一个应用程序,流通过250兆字节的数据,应用一个简单和快速神经网络阈值功能的数据块(其中只有2个32位字每个)。基于结果的(非常简单的)计算,大块是不可预见地推入一个有64个箱。所以这是一个大流和64较短的(变量长度)的流出。

这是重复了很多次与检测不同功能。

计算是记忆带宽的限制。我可以告诉这个,因为没有的速度变化,甚至如果我用别的功能更多的计算集中的。

什么是最好的方式,以结构所写的新的流来优化我的记忆带宽? 我特别想了解缓存使用和缓线的大小可能发挥大的作用。想象的最糟糕的是,我有我的64输出流和通过运气不好,许多地以同一个缓线。然后当我写下一64位的数据流,CPU已经冲洗出一个陈旧的高速缓存行的主存储器,并载在适当的缓线。每个使用64字节的宽带...所以我的带宽限制应用程序可能会浪费95%的存储器的带宽(在这个假设的最坏情况下,虽然).

很难甚至试图衡量的效果,因此设计的方式在它的周围是甚至更含糊不清。或者我甚至是追逐鬼的瓶颈,在某种程度上硬件优化比我更好可以吗?

我使用的核心II x86处理器如果让任何差别。

编辑:这里是一些例子码。它流通过一系列的副本元素,以各种输出阵列挑伪随机。运行的相同程序与不同数量的目的地箱给不同的运行时,即使相同的计算量和存储器中读和写的都做:

2输出流:13秒
8输出流:13秒
32输出流:19秒
128输出流:29秒
512输出流:47秒

之间的差异使用512和2流输出为4倍,(可能??) 由缓行驱逐的开销。

#include <stdio.h>
#include <stdlib.h>
#include <ctime>

int main()
{
  const int size=1<<19;
  int streambits=3;
  int streamcount=1UL<<streambits; // # of output bins
  int *instore=(int *)malloc(size*sizeof(int));
  int **outstore=(int **)malloc(streamcount*sizeof(int *));
  int **out=(int **)malloc(streamcount*sizeof(int));
  unsigned int seed=0;

  for (int j=0; j<size; j++) instore[j]=j;

  for (int i=0; i< streamcount; ++i) 
    outstore[i]=(int *)malloc(size*sizeof(int));

  int startTime=time(NULL);
  for (int k=0; k<10000; k++) {
    for (int i=0; i<streamcount; i++) out[i]=outstore[i];
    int *in=instore;

    for (int j=0; j<size/2; j++) {
      seed=seed*0x1234567+0x7162521;
      int bin=seed>>(32-streambits); // pseudorandom destination bin
      *(out[bin]++)=*(in++);
      *(out[bin]++)=*(in++);
    }

  }
  int endTime=time(NULL);
  printf("Eval time=%ld\n", endTime-startTime);
}
有帮助吗?

解决方案

当你正在写的64出纸槽,你就可以使用多种不同的存储位置。如果仓随机基本填满,这意味着你有时有couls共享同一缓存行的两个区间。不是一个大问题;核心2的L1高速缓存是8路关联。这意味着你会得到一个问题只与9号高速缓存行。在任何时候只有65实时内存引用(1读/写64),8路关联的确定。

L2高速缓存显然是12路关联(3 / 6MB总,所以12不是怪异的数)。所以,即使你不得不在L1的冲突,机会是相当不错的你还是不打主内存。

不过,如果你不喜欢这一点,重新安排在内存中的垃圾箱。相反stroing每个仓顺序,交错他们。对于滨0,存储块0-15在偏移0-63,但店块16-31偏移量8192-8255。对于纸槽1,存储块0-15在偏移64-127,等等。这只需几分钟的移位和口罩,但结果是,一对箱8个共享高速缓存行。

另一种可能的方法在这种情况下,以加快您的代码是SSE4,特别是在64位模式。你会得到16个寄存器×128位,可以优化读取(MOVNTDQA)来限制缓存污染。我不知道这是否会帮助很多与读取速度,但 - 我期望的酷睿预取赶上这一点。读取顺序排列的整数是最简单的一种存取可能的,任何预取器应该优化这一点。

其他提示

你有写你的输出流与内嵌元数据的单个流,以确定每个“块”的选项?如果你阅读“块”,在其上运行您的门限函数,那么而是写你就写,再加上原有数据的特定输出流哪个流它是属于(1个字节),你就应该认真降低抖动。

我不会除了事实,你说你有很多次处理这些数据表明这一点。在每个连续运行的,你看你的输入流,以获得仓室号(1个字节),然后做你需要在接下来的8个字节的仓做。

至于这个机制的cacheing行为,因为你只通过两个数据流滑动,并在所有,但第一种情况下,写尽可能多的数据,你正在阅读,硬件会给你所有的帮助你可能可能希望尽可能预取,高速缓存行优化等。

如果你不得不在每次处理数据时添加额外的字节,你最坏的情况下缓存行为是平均情况。如果你能负担得起的存储命中,这似乎是一场胜利给我。

这里有一些想法,如果你真的绝望...

您可以考虑升级硬件。对于流媒体应用有点类似于你的,我发现我被更改为i7处理器有一个大的提升速度。此外,AMD处理器比酷睿2的内存限制的工作理应更好(虽然我最近没有使用他们自己)。

您可以考虑另一种解决方案是使用像CUDA语言做了图形卡的处理。显卡方面则调整为具有非常高的内存带宽和做快速浮点运算。预计花费5×20倍的开发时间相对CUDA代码的直向前非优化C实现。

您可能希望尝试将文件映射到内存中。这样,内核可以照顾内存管理的为您服务。内核通常最清楚如何处理页面缓存。 这是尤其如此,如果你的应用程序需要在多个平台上运行,为不同的操作系统以不同的方式处理内存管理。

有像ACE( HTTP框架://www.cs.wustl。 EDU /〜施密特/ ACE.html )或升压( http://www.boost.org ),它允许您编写代码,不会在一个独立于平台的方式存储器映射。

真正的答案对于这样的情况是代码了几种方法和时间。你已经很明显完成。所有人喜欢我能做的就是建议的其他方式的尝试。

例如:即使在没有缓颠簸(你的输出流映相同的缓线),如果你正在编写整数的大小,大小=1<<19sizeof(int)=4,32-位--即如果你正在编写8MB的数据,实际上是阅读8MB和随后编写8MB.因为如果你的数据是在普通WB(回写的)存储在一个x86处理,以编写一线您必须首先阅读旧的副本行-即使你是要把数据读的距离。

你可以消除这种不必要的-RFO读通过(a)使用厕所存储器(可能是一个痛苦设立)或(b)使用SSE流的商店,又名NT(非暂时)的商店。MOVNT*-MOVNTQ,MOVNTPS,等等。(还有一个MOVNTDQA流荷载,虽然更多的痛苦来使用。)

我不喜欢本文件我刚刚发现,通过谷歌上搜索 http://blogs.fau.de/hager/2008/09/04/a-case-for-the-non-temporal-store/

现在:MOVNT*适用于世界银行的记忆,但工作就像厕所存、使用的小数量的写cmbining缓冲区。实际数量变化的处理模式:只有4个月的第一个英特尔芯片上有他们,P6(又名奔腾Pro)。当...推土机的4K WCC(写入合并缓)基本上提供了64写入合并缓冲区,每 http://semiaccurate.com/forums/showthread.php?t=6145&page=40, 虽然只有4个经典的卫生间的缓冲区。但 http://www.intel.com/content/dam/doc/manual/64-ia-32-architectures-optimization-manual.pdf 说一些processos有6个卫生间的缓冲区和一些8.无论如何...有几个,但不是很多。通常不是64个。

但在这里的是东西,你可以尝试:实施写入合并你自己。

a)编写一个单一的一套64(#流)的缓冲区,每个小64(缓线的大小),或也许128或256B。让这些缓冲区将在普通WB存储器。你可以访问他们与普通的商店,虽然如果您可以使用MOVNT*,伟大的。

当这些缓冲区得到完全的复制它作为一个突发的地方,在存储器里的流真是应该要走。使用MOVNT*流的商店。

这将最终做 *N bytes储存的临时缓冲区,击中L1缓存 *64*64字节读来填补暂时的缓冲区 *N bytes read从暂时的缓冲区,击中L1缓存。*N字节的书面通过流的商店基本上直接进入存储器。

I.e N bytes缓打读N bytes缓打写+N bytes缓小姐

与N bytes缓近的读N bytes缓写阅读。

减少了N字节的缓错过阅读可以教育部于弥补的额外开销。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top