我正在x86_64上的Linux内核模块中运行以下代码。基本上,我会遍历256位,对于设置为1的每一位,我将其清除并执行某些操作。但是,下面的代码需要花费几千个周期才能运行(瓶颈不是if语句主体中执行的代码)。

unsigned long *data = ...;
for (int i = 0; i < 256; i++) {
    //test_and_clear_bit is a function defined in the Linux kernel
    if (test_and_clear_bit(i, data)) {
        //bit i was set, so do something
    }
}

瓶颈似乎是test_and_clear_bit函数。我要遍历的数据是硬件定义的数据结构,我只能使用读-修改-写指令进行变异(根据Intel手册)。这是因为处理器可能会尝试同时更改数据结构。因此,我不能依靠简单的解决方案,例如使用单个自旋锁保护数据结构,然后仅使用非原子指令读取和清除位。

有更快的方法吗?

最佳答案

这是一个很难回答的问题,因为我们不确切知道这些数据是什么,并且由于以下语句:



就是说,我们能做的最好的就是一般性的想法/建议,这可能适用于您的具体情况。您可以尝试以下操作:

  • data复制到本地缓冲区中并迭代这些位,仅在该位在本地缓冲区中设置的情况下才调用test_and_clear_bit。这样可以避免在本地缓冲区尚未设置的位上调用test_and_clear_bit。显然,未在本地缓冲区中设置的位可能已经在结构的复制和执行之间设置了,但是如果这是可以接受的丢失,则可能会大大加快循环速度。
  • 如果可能,一次测试多个位。就像@immibis在评论中提到的那样,如果您一次可以检查8、16、32或64位,那么只有从多位集获得响应时才测试单个位。如果很有可能每8个或更多位中至少设置了一位,则此操作将不起作用,并且由于会增加不必要的调用,因此实际上会减慢循环速度。
  • 使用test_and_clear_bit尝试自己的 volatile实现,就像@IlyaBursov在评论中提到的那样。这不能保证能正常工作,并且在一个平台或编译器上可能不能在另一个平台上工作的东西。但是,如果您使用的是硬件定义的内存结构,则特定于平台的解决方案可能适合您。请注意,volatile不太可能防止此处理器修改位,但是在某些平台上(如果幸运的话,可能是这样)。作为mentioned here:

  • 08-25 17:03