我正在用c ++编写多线程应用程序,其中性能至关重要.我需要在线程之间复制小结构时使用大量锁定,为此我选择使用自旋锁.
我已经做了一些研究和速度测试,我发现大多数实现大致同样快:
__asm {}使用类似这样的代码,它得分约70个时间单位,但我不确定是否已创建适当的内存屏障.编辑:这里给出的时间是2个线程锁定和解锁螺旋锁1,000,000次所需的时间.
我知道这并没有太大的区别,但是由于自旋锁是一个使用频繁的对象,人们会认为程序员会同意以最快的方式制作自旋锁.谷歌搜索导致许多不同的方法.我认为如果使用内联汇编并使用指令而不是比较32位寄存器来实现上述方法将是最快的CMPXCHG8B.此外,必须考虑内存障碍,这可以通过LOCK CMPXHG8B(我认为?)来完成,这保证了内核之间共享内存的"专有权".最后[有人建议]对于繁忙的等待应该伴随NOP:REP,这将使超线程处理器切换到另一个线程,但我不确定这是否是真的?
根据我对不同螺旋锁的性能测试,可以看出没有太大区别,但出于纯粹的学术目的,我想知道哪一个是最快的.但是由于我在汇编语言和内存障碍方面的经验非常有限,如果有人可以为我在LOCK CMPXCHG8B中提供的最后一个示例编写汇编代码并在以下模板中使用适当的内存屏障,我会很高兴:
__asm
{
spin_lock:
;locking code.
spin_unlock:
;unlocking code.
}
Run Code Online (Sandbox Code Playgroud) 假设一个核心中的线程正在旋转一个变量,该变量将由另一个核心上运行的线程更新.我的问题是缓存级别的开销是多少.等待线程是否会缓存变量,因此在写入线程写入该变量之前不会导致总线上的任何流量?
如何减少这种开销.x86 pause指令有帮助吗?
如果我有这样的东西......
volatile long something_global = 0;
long some_public_func()
{
return something_global++;
}
Run Code Online (Sandbox Code Playgroud)
当使用多个线程访问时,期望此代码不会中断(竞争条件)是否合理?如果它不是标准的,它仍然可以作为现代编译器的合理假设吗?
注意:所有我使用它的原因是原子增量和减量 - 没有什么比这更好的了.
我想知道编译器有什么保证可以确保对内存的线程写入在其他线程中具有可见效果.
我知道无数的情况,这是有问题的,我敢肯定,如果你有兴趣回答你也知道它,但请关注我将要呈现的案件.
更确切地说,我担心可能导致线程丢失其他线程完成的内存更新的情况.我不关心(此时)更新是非原子的还是非常同步的:只要相关的线程注意到更改,我会很高兴.
我希望编译器区分两种变量访问:
例如,如果您使用此代码段:
void sleepingbeauty()
{
int i = 1;
while (i) sleep(1);
}
Run Code Online (Sandbox Code Playgroud)
既然i是本地的,我认为我的编译器可以优化它,只是让沉睡的美丽陷入永恒的沉睡.
void onedaymyprincewillcome(int* i);
void sleepingbeauty()
{
int i = 1;
onedaymyprincewillcome(&i);
while (i) sleep(1);
}
Run Code Online (Sandbox Code Playgroud)
既然i是本地的,但是它的地址被接受并传递给另一个函数,我假设我的编译器现在知道它是一个"可寻址的"变量,并生成内存读取以确保可能有一天王子会来.
int i = 1;
void sleepingbeauty()
{
while (i) sleep(1);
}
Run Code Online (Sandbox Code Playgroud)
由于i是全局的,我假设我的编译器知道变量有一个地址,并将生成读取而不是缓存该值.
void sleepingbeauty(int* ptr)
{
*ptr = 1;
while (*ptr) sleep(1);
}
Run Code Online (Sandbox Code Playgroud)
我希望dereference运算符足够明确,让我的编译器在每次循环迭代时生成一个内存读取.
我很确定这是生产中每个C和C++编译器使用的内存访问模型,但我认为没有任何保证.事实上,C++ 03甚至对线程的存在视而不见,所以这个问题在标准时甚至都没有意义.不过我对C不太确定.
是否有一些文件指明我是对还是错?我知道这些都是泥泞的水域,因为这些可能不符合标准,对我来说这似乎是一个重要的问题.
除了编译器生成读取之外,我还担心CPU缓存在技术上可能会保留一个过时的值,即使我的编译器尽力使读取和写入,值也不会在线程之间同步.这会发生吗?
在程序集中实现自旋锁.在这里,我发布了一个我提出的解决方案.这是对的吗?你知道一个较短的吗?
锁:
mov ecx, 0
.loop:
xchg [eax], ecx
cmp ecx, 0
je .loop
Run Code Online (Sandbox Code Playgroud)
发布:
lock dec dword [eax]
Run Code Online (Sandbox Code Playgroud)
eax初始化为-1(这意味着锁是免费的).这适用于许多线程(不一定是2).