是否可以锁定CPU缓存中的一些数据?

时间:2009-10-06 14:17:34

标签: c++ cpu-cache

我有问题.... 我正在while循环中将数据写入数组。关键是我经常这样做。似乎这篇文章现在是代码中的瓶颈。所以我认为它是由写入内存引起的。这个数组不是很大(像300个元素一样)。问题是可以这样做:将它存储在缓存中并仅在while循环完成后才在内存中更新?

[编辑 - 从Alex添加的答案中复制]

double* array1  = new double[1000000]; // this array has elements  
unsigned long* array2  = unsigned long[300];
double varX,t,sum=0;
int iter=0,i=0;
while(i<=max_steps)
{
   varX+=difX;
   nm0 =  int(varX);
   if(nm1!=nm0)
   {
        array2[iter] = nm0;  // if you comment this string application works more then 2 times faster :)
        nm1=nm0;
        t = array1[nm0]; // if you comment this string , there is almost no change in time 
        ++iter;
   }
   sum+=t;
   ++i;
}

首先,我要感谢你们所有人的答案。实际上,不放置代码有点蠢。所以我现在决定这样做。

double* array1  = new double[1000000]; // this array has elements  
unsigned long* array2  = unsigned long[300];
double varX,t,sum=0;
int iter=0,i=0;
while(i<=max_steps)
{
   varX+=difX;
   nm0 =  int(varX);
   if(nm1!=nm0)
   {
        array2[iter] = nm0;  // if you comment this string application works more then 2 times faster :)
        nm1=nm0;
        t = array1[nm0]; // if you comment this string , there is almost no change in time 
        ++iter;
   }
   sum+=t;
   ++i;
}

就是这样。如果有人有任何想法会很好。再次感谢你。

此致 亚历

12 个答案:

答案 0 :(得分:13)

不是故意的,没有。除此之外,你不知道缓存有多大,所以你不知道什么是适合的。此外,如果允许应用程序锁定部分缓存,则对操作系统的影响可能会对整体系统性能造成破坏。这完全属于我的“你不能这样做,因为你不应该这样做。永远。”

你可以做的是改善你的引用位置 - 尝试安排循环,这样你就不会多次访问这些元素,并尝试在内存中按顺序访问它们。

如果没有关于您的申请的更多线索,我认为不会给出更具体的建议。

答案 1 :(得分:7)

CPU通常不提供细粒度的缓存控制,不允许您选择被驱逐的内容或将内容固定在缓存中。您在某些CPU上有一些缓存操作。就像你可以做的一些信息一样:这里有一些关于较新的x86 {-64} CPU的一些有趣的缓存相关指令(做这样的事情使得可移植性地狱,但我认为你可能很好奇)

Software Data Prefecth

  

非时间指令是   prefetchnta,用于获取数据   进入二级缓存,   最大限度地减少缓存污染。

     

时间指令如下   如下:

* prefetcht0 – fetches the data into all cache levels, that is, to the
     

奔腾®4处理器的二级缓存。

* prefetcht1 – Identical to prefetcht0

* prefetcht2 – Identical to prefetcht0

此外,还有一组用于访问内存中数据的指令,但明确告诉处理器不要将数据插入缓存中。这些被称为非时间指令。这里有一个例子:MOVNTI

您可以对缓存中不需要的每个数据使用非时间指令,希望其余数据始终保留在缓存中。我不知道这是否会真正提高性能,因为当涉及缓存时需要注意微妙的行为。这听起来像是相对痛苦的。

答案 2 :(得分:3)

除非您的代码在写入数组之间做了完全不同的事情,否则大部分数组可能会保留在缓存中。

不幸的是,除了用缓存重写算法之外,你无法做任何事情来影响缓存中的内容。尝试在写入内存之间使用尽可能少的内存:不要使用很多变量,不要调用许多其他函数,并尝试连续写入数组的同一区域。

答案 3 :(得分:3)

  

我遇到了问题....我在while循环中将数据写入数组。关键是我经常这样做。似乎这篇文章现在是代码中的瓶颈。所以我认为它是由写入内存引起的。这个数组不是很大(像300个元素一样)。问题是可以这样做:将它存储在缓存中并仅在while循环结束后才在内存中更新?

你不需要。将推出缓存的唯一原因是,如果认为某些其他数据更加紧急,可以放入缓存中。

除此之外,300个元素的数组应该放在缓存中没有问题(假设元素大小不是太疯狂),所以很可能,你的数据已经在cach中。

无论如何,最有效的解决方案可能是调整你的代码。使用大量的临时值(向编译器指示内存地址并不重要),而不是不断地写入/读入数组。重新排序代码,以便在循环开始时执行一次加载,并尽可能地分解依赖链。

手动展开循环可以让您更灵活地实现这些目标。

最后,您应该使用两个明显的工具,而不是猜测缓存行为:

  • 分析器和cachegrind(如果可用)。一个好的分析器可以告诉你很多关于缓存未命中的统计信息,而cachegrind也会给你很多信息。
  • 我们在StackOverflow。如果您发布循环代码并询问如何改进其性能,我相信我们很多人会发现它是一个有趣的挑战。

但正如其他人所提到的,在处理表演时,不要猜测。你需要硬数据和测量,而不是直觉和直觉。

答案 4 :(得分:2)

我怀疑这是可能的,至少在高级多任务操作系统上是这样。您不能保证您的进程不会被抢占,并且会丢失CPU。如果你的进程拥有缓存,那么其他进程就无法使用它,这会使它们的执行变得非常缓慢,并使事情变得复杂很多。你真的不想运行没有缓存的现代几GHz处理器,因为一个应用程序已将所有其他应用程序锁定在其中。

答案 5 :(得分:2)

在这种情况下,array2将非常“热”并且仅仅因为这个原因而留在缓存中。诀窍是让array1不在缓存中(!)。你只读了一次,所以没有必要缓存它。 SSE指令是MOVNTPD,内在void_mm_stream_pd(double *destination, __m128i source)

答案 6 :(得分:1)

即使你可以,也是一个坏主意。

现代台式电脑使用多核CPU。英特尔的芯片是台式机中最常见的芯片......但Core和Core 2处理器不共享片上缓存。

也就是说,在Core 2 i7芯片发布之前没有共享缓存,它共享一个8MB的L3缓存。

因此,如果您能够锁定我正在键入的计算机上的缓存中的数据,您甚至无法保证此进程将在同一核心上进行调度,因此缓存锁定可能完全无用。

答案 7 :(得分:1)

如果写入速度很慢,请确保没有其他CPU内核同时写入同一内​​存区域。

答案 8 :(得分:1)

也许可以使用一些汇编代码,或者像onebyone指出的那样,汇编内在函数来预取内存行到缓存中,但这需要花费大量时间来修补它。

只是为了试用,尝试读入所有数据(以编译器不会优化的方式),然后执行写操作。看看是否有帮助。

答案 9 :(得分:1)

如果遇到性能问题,请不要假设,先测量。例如,注释掉写入,并查看性能是否有所不同。

如果要写入结构数组,请使用结构指针来缓存结构的地址,这样每次访问时都不会对数组进行乘法运算。确保使用数组索引器变量的本机字长来进行最大程度的优化。

答案 10 :(得分:1)

正如其他人所说,你不能直接控制它,但改变你的代码可能会间接地实现更好的缓存。如果您在Linux上运行并希望在程序运行时更深入地了解CPU缓存发生的情况,则可以使用Cachegrind工具,它是Valgrind套件的一部分。它是一个处理器的模拟,因此它不是完全真实的,但它为您提供了难以获得的信息。

答案 11 :(得分:1)

在CoreBoot(以前称为LinuxBIOS)的早期启动阶段,因为他们还没有访问RAM(我们正在讨论BIOS代码,因此RAM还没有初始化),他们设置了一些他们称之为Cache-as的东西。 -RAM(CAR),即使没有实际RAM支持,它们也将处理器缓存用作RAM。