【问题标题】:Is it possible to lock some data in CPU cache?是否可以将某些数据锁定在 CPU 缓存中?
【发布时间】:2009-10-06 14:17:34
【问题描述】:

我有问题.... 我在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;
}

原来如此。如果有人有任何想法,那就太好了。再次非常感谢。

真诚的 亚历克斯

【问题讨论】:

标签: c++ cpu-cache


【解决方案1】:

不是故意的,不是。除其他外,您不知道缓存有多大,因此您不知道要适应什么。此外,如果允许应用程序锁定部分缓存,对操作系统的影响可能会对整体系统性能造成毁灭性影响。这完全属于我的清单“你不能这样做,因为你不应该这样做。永远。”

您可以做的是改善您的参考位置 - 尝试安排循环,以便您不会多次访问元素,并尝试在内存中按顺序访问它们。

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

【讨论】:

  • 这并不完全正确。显然,细节将是特定于体系结构的(因此使“c++”完全成为这个问题的错误标签),但现代 CPU 确实提供了对缓存和主内存中存储内容的一些控制。实际上,ARM9 CPU 有一个实际的“锁定”API,与提问者的要求相匹配。英特尔盒子受到更多限制,您可以在需要之前发出指令预取行,并使用 MFENCE 指令来控制写回脏缓存行的顺序。
【解决方案2】:

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

您可以对您不希望缓存中的每条数据使用非临时指令,希望其余部分始终保留在缓存中。我不知道这是否真的会提高性能,因为在缓存方面需要注意一些微妙的行为。而且听起来会比较痛苦。

【讨论】:

    【解决方案3】:

    我有一个问题....我在 while 循环中将数据写入数组。关键是我经常这样做。看来,这种写作现在是代码中的瓶颈。所以我认为这是由写入内存引起的。这个数组并不是很大(比如 300 个元素)。问题是可以这样做:将其存储在缓存中并仅在while循环完成后在内存中更新?

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

    除此之外,包含 300 个元素的数组应该可以毫无问题地放入缓存中(假设元素大小不是太疯狂),因此很可能您的数据已经在缓存中。

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

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

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

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

    但正如其他人所提到的,在处理性能时,不要猜测。您需要可靠的数据和测量结果,而不是预感和直觉。

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          在这种情况下,array2 将非常“热”并仅出于这个原因而留在缓存中。诀窍是将array1 保持在缓存之外(!)。你只读过一次,所以缓存它没有意义。 SSE 指令是MOVNTPD,内在void_mm_stream_pd(double *destination, __m128i source)

          【讨论】:

            【解决方案7】:

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

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

            也就是说,在 Core 2 i7 芯片发布之前不共享缓存,该芯片共享一个片上 8MB L3 缓存。

            因此,如果您能够将数据锁定在我正在输入此内容的计算机上的缓存中,那么您甚至无法保证此进程将被安排在同一个内核上,因此缓存锁定可能完全没用。

            【讨论】:

              【解决方案8】:

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

              【讨论】:

                【解决方案9】:

                当您遇到性能问题时,不要假设任何事情,先进行测量。例如,将写入注释掉,看看性能是否有所不同。

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

                【讨论】:

                  【解决方案10】:

                  正如其他人所说,您无法直接控制这一点,但更改代码可能会间接启用更好的缓存。如果您在 linux 上运行并希望在程序运行时更深入地了解 CPU 缓存发生了什么,您可以使用 Cachegrind 工具,它是Valgrind 套件的一部分。这是一个处理器的模拟,所以它并不完全真实,但它为您提供了其他任何方式都难以获得的信息。

                  【讨论】:

                    【解决方案11】:

                    也许可以使用一些汇编代码,或者正如有人指出的,汇编内在函数,将内存行预取到缓存中,但这将花费大量时间来修补它。

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

                    【讨论】:

                    • 你不一定要使用汇编。例如 GCC 有__builtin_prefetch 来提供缓存提示。
                    • @onebyone 好点,但这仍然归结为 PREFETCH 指令
                    【解决方案12】:

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

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2018-02-02
                      • 2021-03-08
                      • 2015-05-26
                      • 2018-05-29
                      • 2013-06-24
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多