- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在x86-64
平台上,CLFLUSH
汇编指令允许刷新与给定地址相对应的缓存行。除了刷新与特定地址相关的缓存之外,还有一种方法可以刷新整个缓存(与正在执行的程序相关的缓存或整个缓存),例如通过使其充满虚拟内容(或任何其他内容)我不会意识到的其他方法):
void flush_cache()
{
// Contents
}
最佳答案
有关与清除缓存有关的问题的链接(尤其是在x86上),请参阅WBINVD instruction usage上的第一个答案。
不,使用纯ISO C++ 17 无法可靠或有效地执行此操作。它不知道也不关心CPU缓存。您可能要做的最好的事情就是触摸大量内存,这样其他所有东西最终都会被驱逐1,但这并不是您真正想要的。 (当然,根据定义,刷新所有缓存效率不高...)
CPU缓存管理功能/内在函数/asm指令是C++语言特定于实现的扩展。但是除了内联汇编之外,我所知道的C或C++实现都没有提供刷新所有缓存(而不是地址范围)的方法。那是因为这不是正常的事情。
例如,在x86上,您要查找的asm指令为wbinvd
。 它在驱逐之前写回所有脏行,这与invd
(它会丢弃高速缓存而不写回useful when leaving cache-as-RAM mode)不同。因此,从理论上讲wbinvd
没有体系结构作用,只有微体系结构,但是它是如此之慢以至于它是一种特权指令。正如Intel's insn ref manual entry for wbinvd
指出的那样,这将增加中断等待时间,因为它本身是不可中断的,可能必须等待8 MiB或更多的脏L3缓存被刷新。也就是说,与大多数时序效果不同,将中断延迟这么长时间可以视为一种架构效果。在多核系统上,它也很复杂,因为它必须刷新所有核的缓存。
我认为无法在x86的用户空间(第3环)中使用它。与cli
/sti
和in
/out
不同,它不是由IO特权级别启用的(您可以在Linux上使用 iopl()
system call进行设置)。因此,wbinvd
仅在实际在环0(即内核代码)中运行时有效。参见Privileged Instructions and CPU Ring Levels。
但是,如果您正在用GNU C或C++编写内核(或在ring0中运行的独立程序),则可以使用asm("wbinvd" ::: "memory");
。在运行实际DOS的计算机上,普通程序在实模式下运行(没有任何低特权级别;所有内容实际上都是内核)。那是运行微基准测试的另一种方式,该微基准测试需要运行特权指令来避免wbinvd
的内核<->用户空间转换开销,并且还具有在OS下运行的便利,因此您可以使用文件系统。不过,将微基准标记放入Linux内核模块可能比从USB内存棒等启动FreeDOS容易。特别是如果您想控制涡轮频率的东西。
我能想到的,您可能想要这样做的唯一原因是为了进行某种实验,以弄清楚特定CPU的内部结构是如何设计的。因此,确切的操作细节至关重要。我什至不想要一种便携式/通用的方法来做到这一点。
或者在重新配置物理内存布局之前,例如在内核中。因此,现在有一个用于以太网卡的MMIO区域,该区域以前是普通的DRAM。但是在那种情况下,您的代码已经完全是特定于拱的。
通常,出于正确性原因,您希望/需要刷新缓存时,您知道哪个地址范围需要刷新。例如在具有不具有缓存一致性的DMA的体系结构上编写驱动程序时,写回操作会在DMA读取之前发生,并且不会逐步进行DMA写操作。 (逐出部分对于DMA读取也很重要:您不希望使用旧的缓存值)。但是x86如今已经具有与缓存相关的DMA,因为现代设计将内存 Controller 内置到CPU芯片中,因此系统流量可以在从PCIe到内存的途中窥探L3。
在驱动程序之外,您需要担心缓存的主要情况是在具有非一致性指令缓存的非x86架构上使用JIT代码生成。如果您(或JIT库)将一些机器代码写入char[]
缓冲区并将其强制转换为函数指针,则ARM之类的体系结构无法保证代码提取将“看到”新写入的数据。
这就是为什么gcc提供 __builtin__clear_cache
的原因。它不一定刷新任何东西,只是确保以代码形式执行该内存是安全的。 x86具有与数据高速缓存一致的指令高速缓存,并支持self-modifying code,而无需任何特殊的同步指令。请参见godbolt for x86 and AArch64,并注意__builtin__clear_cache
对于x86编译为零指令,但对周围的代码有影响:没有它,gcc可以在将其转换为函数指针并调用之前优化将存储移至缓冲区。 (它没有意识到数据已被用作代码,因此认为它们已死存储并消除了它们。)
尽管名称,__builtin__clear_cache
与wbinvd
完全无关。它需要一个地址范围(如args),因此不会刷新并使整个缓存无效。它还不使用clflush
,clflushopt
或clwb
从缓存中实际写回(或可选地逐出)数据。
当需要刷新某些高速缓存以确保正确性时,您只想刷新一定范围的地址,而不希望通过刷新所有缓存来减慢系统速度。
出于性能原因,至少在x86 上故意刷新缓存几乎是没有道理的。有时,您可以使用污染最小化的预取来读取数据而不会造成太多的缓存污染,或者使用NT存储区来写缓存。但是,在正常情况下,最后一次触摸某些内存后再执行“常规”操作,然后执行clflushopt
通常是不值得的。就像存储一样,它必须一直遍历整个内存层次结构,以确保在任何地方都能找到并刷新该行的任何副本。
没有像_mm_prefetch
相反的轻量级指令设计为性能提示。
您可以在x86上的用户空间中执行的唯一缓存刷新操作是clflush
/clflushopt
。 (或者使用NT存储,如果存储行之前很热,它们也会驱逐该缓存行)。或者当然是针对已知的L1d大小和关联性创建冲突驱逐,例如以4kiB的倍数写入多行,所有这些行都映射到32k/8路L1d中的同一集合。
有一个用于 _mm_clflush(void const *p)][6]
的Intel内在[clflush
包装器(另一个是 clflushopt
),但它们只能按(虚拟)地址刷新缓存行。您可以遍历您的进程已映射的所有页面中的所有缓存行...(但是那只能刷新您自己的内存,而不能刷新正在缓存内核数据的缓存行,例如您的进程的内核堆栈或其task_struct
,与您刷新所有内容相比,第一个系统调用仍然会更快。
有一个Linux系统调用包装器可移植地逐出一系列地址: cacheflush(char *addr, int nbytes, int flags)
。如果x86完全支持x86上的实现,则大概在循环中使用clflush
或clflushopt
。手册页说它首先出现在MIPS Linux中,但是
如今,Linux在其他一些操作系统上提供了cacheflush()系统调用
架构,但有不同的论点。”
我认为没有Linux系统调用可以公开wbinvd
和,但是您可以编写一个内核模块来添加一个模块。
最近的x86扩展引入了更多的缓存控制指令,但仍然只能通过地址来控制特定的缓存行。用例适用于non-volatile memory attached directly to the CPU,例如Intel Optane DC Persistent Memory。如果要提交持久性存储而不会使下一个读取变慢,则可以使用 clwb
。但是请注意,clwb
不能保证避免驱逐,只是允许这样做。它可能与may be the case on SKX一样与clflushopt
运行。
请参阅https://danluu.com/clwb-pcommit/,但请注意,不需要pcommit
:Intel决定在发布任何需要它的芯片之前简化ISA,因此clwb
或clflushopt
+ sfence
就足够了。参见https://software.intel.com/en-us/blogs/2016/09/12/deprecate-pcommit-instruction。
无论如何,这是一种与现代CPU相关的缓存控制。无论您正在进行什么实验,都需要ring0并在x86上进行组装。
脚注1:占用大量内存:纯ISO C++ 17
您可能会分配一个非常大的缓冲区,然后对其进行memset
(这样,这些写入将使用该数据污染所有(数据)缓存),然后取消映射。如果delete
或free
实际上立即将内存返回给操作系统,那么它将不再是您进程的地址空间的一部分,因此其他数据中只有少数高速缓存行仍然很热:可能是一两行堆栈(假设您正在使用C++实现,该实现使用堆栈,并且在OS下运行程序...)。当然,这只会污染数据缓存,而不污染指令缓存,并且正如Basile指出的那样,某些级别的缓存是每个内核专用的,并且OS可以在CPU之间迁移进程。
另外,请注意,使用实际的memset
或std::fill
函数调用或为此优化的循环可以优化为使用绕过缓存或减少污染的商店。而且我还隐式地假设您的代码在具有写分配缓存的CPU上运行,而不是在存储未命中时直写(因为所有现代CPU都是以这种方式设计的)。
进行一些无法优化并占用大量内存的操作(例如使用long
数组而不是位图的主筛)会更可靠,但当然仍然依赖于缓存污染来驱逐其他数据。仅仅读取大量数据也不可靠。一些CPU实现了自适应替换策略,以减少顺序访问带来的污染,因此,希望在大型阵列上循环不会驱逐大量有用数据。例如。 the L3 cache in Intel IvyBridge and later执行此操作。
关于c++ - 有没有办法刷新与程序相关的整个CPU缓存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48527189/
我是 C 语言新手,我编写了这个 C 程序,让用户输入一年中的某一天,作为返回,程序将输出月份以及该月的哪一天。该程序运行良好,但我现在想简化该程序。我知道我需要一个循环,但我不知道如何去做。这是程序
我一直在努力找出我的代码有什么问题。这个想法是创建一个小的画图程序,并有红色、绿色、蓝色和清除按钮。我有我能想到的一切让它工作,但无法弄清楚代码有什么问题。程序打开,然后立即关闭。 import ja
我想安装screen,但是接下来我应该做什么? $ brew search screen imgur-screenshot screen
我有一个在服务器端工作的 UDP 套接字应用程序。为了测试服务器端,我编写了一个简单的 python 客户端程序,它发送消息“hello world how are you”。服务器随后应接收消息,将
我有一个 shell 脚本,它运行一个 Python 程序来预处理一些数据,然后运行一个 R 程序来执行一些长时间运行的任务。我正在学习使用 Docker 并且我一直在运行 FROM r-base:l
在 Linux 中。我有一个 c 程序,它读取一个 2048 字节的文本文件作为输入。我想从 Python 脚本启动 c 程序。我希望 Python 脚本将文本字符串作为参数传递给 c 程序,而不是将
对于一个类,我被要求编写一个 VHDL 程序,该程序接受两个整数输入 A 和 B,并用 A+B 替换 A,用 A-B 替换 B。我编写了以下程序和测试平台。它完成了实现和行为语法检查,但它不会模拟。尽
module Algorithm where import System.Random import Data.Maybe import Data.List type Atom = String ty
我想找到两个以上数字的最小公倍数 求给定N个数的最小公倍数的C++程序 最佳答案 int lcm(int a, int b) { return (a/gcd(a,b))*b; } 对于gcd,请查看
这个程序有错误。谁能解决这个问题? Error is :TempRecord already defines a member called 'this' with the same paramete
当我运行下面的程序时,我在 str1 和 str2 中得到了垃圾值。所以 #include #include #include using namespace std; int main() {
这是我的作业: 一对刚出生的兔子(一公一母)被放在田里。兔子在一个月大时可以交配,因此在第二个月的月底,每对兔子都会生出两对新兔子,然后死去。 注:在第0个月,有0对兔子。第 1 个月,有 1 对兔子
我编写了一个程序,通过对字母使用 switch 命令将十进制字符串转换为十六进制,但是如果我使用 char,该程序无法正常工作!没有 switch 我无法处理 9 以上的数字。我希望你能理解我,因为我
我是 C++ 新手(虽然我有一些 C 语言经验)和 MySQL,我正在尝试制作一个从 MySQL 读取数据库的程序,我一直在关注这个 tutorial但当我尝试“构建”解决方案时出现错误。 (我正在使
仍然是一个初学者,只是尝试使用 swift 中的一些基本函数。 有人能告诉我这段代码有什么问题吗? import UIKit var guessInt: Int var randomNum = arc
我正在用 C++11 编写一个函数,它采用 constant1 + constant2 形式的表达式并将它们折叠起来。 constant1 和 constant2 存储在 std::string 中,
我用 C++ 编写了这段代码,使用运算符重载对 2 个矩阵进行加法和乘法运算。当我执行代码时,它会在第 57 行和第 59 行产生错误,非法结构操作(两行都出现相同的错误)。请解释我的错误。提前致谢:
我是 C++ 的初学者,我想编写一个简单的程序来交换字符串中的两个字符。 例如;我们输入这个字符串:“EXAMPLE”,我们给它交换这两个字符:“E”和“A”,输出应该类似于“AXEMPLA”。 我在
我需要以下代码的帮助: 声明 3 个 double 类型变量,每个代表三角形的三个边中的一个。 提示用户为第一面输入一个值,然后 将用户的输入设置为您创建的代表三角形第一条边的变量。 将最后 2 个步
我是新来的,如果问题不好请见谅 任务:将给定矩阵旋转180度 输入: 1 4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 输出: 16 15 14 13 12 11
我是一名优秀的程序员,十分优秀!