我想在运行Linux的ARMv8-A平台上禁用低级缓存,以便测量优化代码的性能,而不依赖于缓存访问。
对于Intel系统,我发现了以下资源(Is there a way to disable CPU cache (L1/L2) on a Linux system?),但由于指令集不同,我无法直接应用。
到目前为止,我有一个内核模块,它会改变相应的系统寄存器以禁用指令和数据缓存。
#include <linux/module.h>
int init_module(void)
{
int64_t value;
asm volatile("\
MRS %0, SCTLR_EL1 // Read SCTLR_EL1 into Xt\n\
BIC %0, %0, (1<<2) // clear bit 2, SCTLR_EL1.C\n\
BIC %0, %0, (1<<12) // clear bit 12, SCTLR_EL1.I\n\
MSR SCTLR_EL1, %0 // Write Xt to SCTLR_EL1\n\
" : "+r" (value));
return 0;
}
void cleanup_module(void)
{
int64_t value;
asm volatile("\
MRS %0, SCTLR_EL1 // Read SCTLR_EL1 into Xt\n\
ORR %0, %0, (1<<2) // set bit 2, SCTLR_EL1.C\n\
ORR %0, %0, (1<<12) // set bit 12, SCTLR_EL1.I\n\
MSR SCTLR_EL1, %0 // Write Xt to SCTLR_EL1\n\
": "+r" (value));
}
MODULE_LICENSE("GPL");
然而,它会在加载时导致完整的系统冻结(当我在系统寄存器中设置相应的位时)。我的猜测是我仍然需要某种缓存,但我在ARM手册中找不到任何有用的东西。
任何人都有一些有用的提示我如何成功禁用ARM上的缓存或我在这里缺少什么?感谢。
答案 0 :(得分:4)
总的来说,由于几个原因,这是行不通的。
首先,清除SCTLR.C位只会使所有数据访问都不可缓存。并阻止分配到任何缓存中。缓存中的任何数据仍然存在于缓存中,尤其是来自最近编写的任何内容的脏行;考虑当你的函数返回并且调用者试图恢复一个甚至不存在于它现在正在访问的内存中的堆栈帧时会发生什么。
其次,单处理器ARMv8系统很少;假设您正在运行SMP Linux,并且突然禁用了模块加载器碰巧安排的任何CPU上的缓存,那么即使忽略第一点,事情也会非常快速地下降。 Linux期望所有CPU彼此保持一致,并且如果违反该假设,通常会非常迅速地破坏。请注意,它甚至不值得冒险进入SMP交叉呼叫;足以说明,即使尝试在禁用缓存的情况下运行Linux,唯一的安全方法是确保它们永远不会启用,除了......
第三,无法保证Linux甚至可以在禁用缓存的情况下运行。在当前硬件上,内核中的所有锁定和原子操作(更不用说用户空间)都依赖于独占访问指令。虽然CPU集群将为可缓存内存实现体系结构所需的本地和全局独占监视器(通常作为缓存机制本身的一部分),但是依赖于系统是否实现了用于非可缓存访问的全局独占监视器因为这样的东西必须在CPU外部(通常在互连或内存控制器中)。许多系统都没有实现这样的全局监视器,在这种情况下,对外部存储器的独占访问可能会发生故障,不执行任何操作或其他各种实现定义的行为,这些行为将导致Linux崩溃或死锁。在这样的系统上运行带有缓存的Linux实际上是不可能的 - 为了让UP arm64内核工作(SMP实际上是不可能的)的黑客攻击数量是不切实际的;祝用户空间好运。
尽管如此,最糟糕的问题不在于此,而是:
...为了衡量优化代码的性能,与缓存访问无关。
如果代码打算在禁用缓存的部署中运行,那么从逻辑上讲它不能在Linux下运行,因此用于黑客攻击Linux的工作将更好地用于更真实的执行中的基准测试环境,以便结果实际上具有代表性。另一方面,如果 意图在启用缓存的情况下运行(在Linux或任何其他操作系统下),则禁用缓存的基准测试将产生无意义的结果并浪费时间。 &#34;优化&#34;例如在实践中不存在的指令获取带宽瓶颈不会引导你朝着正确的方向发展。
答案 1 :(得分:0)
我已经在armv8-a linux上完成了。我这样做不是为了衡量性能,而是要验证xilinx zcu104平台可能存在潜在的一致性错误。结果,提供的Pynq图像xilinx在pl和ps通信期间必须具有一些相干误差。 这是我的解决方法:
我的平台是cortex-a53,ubuntu18从EL2开始并切换到EL1,并在四个cpu内核上支持SMP。因此,我需要关闭多核以确保二级缓存的一致性。多亏了cpu-hot-plug的功能,我才运行:
echo'0'> / sys / devices / system / cpu / cpu1 / online,
echo'0'> / sys / devices / system / cpu / cpu2 / online,
回显'0'> / sys / devices / system / cpu / cpu3 / online
然后我运行dmesg以验证多核已关闭。
我构建了内核源代码树,因为在Linux中找不到它。您可以运行uname -r来查看您的内核版本。并在/ usr / src中查找以查看您的Linux是否已经拥有它。
我构建了linux模块。使用gcc inline asm,我刷新所有缓存并设置sctlr_el1.c0。
我将模块插入。而且我首先以正确的结果运行我的程序,尽管它比使用多核和D缓存要慢20倍。