CPU 指令重新排序
CPU instruction reordering
我们的处理器可以对指令重新排序以获得一些性能优势,但这可能会导致一些奇怪的行为。我正在尝试在本文的基础上重现此问题之一。
这是我的代码:
int a,b;
int r1,r2;
mutex m1,m2;
void th1()
{
for(;;)
{
m1.lock();
a=1;
asm volatile("" ::: "memory");
r1=b;
m1.unlock();
}
}
void th2()
{
for(;;)
{
m2.lock();
b=1;
asm volatile("" ::: "memory");
r2=a;
m2.unlock();
}
}
int main()
{
int cnt{0};
thread thread1{th1};
thread thread2{th2};
thread1.detach();
thread2.detach();
for(int i=0;i<10000;i++)
{
m1.lock();
m2.lock();
a=b=0;
m1.unlock();
m2.unlock();
if(r1==0&&r2==0)
{
++cnt;
}
}
cout<<cnt<<" CPU reorders happened!n";
}
我使用互斥锁来确保"主"线程在 th1 或 th2 执行其工作时不会修改 a 或 b,执行的输出一直在变化,它可能是 0,可能是 10000 或 0 到 10000 之间的随机数字。
这段代码有一些东西让我有点不舒服,我不确定它是否真的重现了 CPU 重新排序现象。
从代码看来,r1 和 r2 在"if"中可能为 0 的唯一方式是因为 th1 和 th2 将它们设置为"a"和"b"的值,在 th1 和 th2 的上下文中由于锁定机制不能为 0,这些变量为 0 的唯一方法是因为指令重新排序, 这个对吗?
谢谢
您的程序与您从 preshing.com 引用的文章中的程序非常不同。 preshing.com 程序使用信号量,而您的程序使用互斥体。
互斥体比信号量更简单。 它们只做一个保证 - 一次只有一个线程可以锁定互斥锁。 也就是说,它们只能用于相互排斥。
preshing.com 程序使用其信号量做一些单独使用互斥锁无法做到的事情:它同步三个线程中的循环,以便它们都以锁步方式进行。 Thread1 和 Thread2 各自在循环的顶部等待,直到 main() 允许它们离开,然后 main 在其循环的底部等待,直到它们完成工作。 然后他们又都转了一圈。
你不能用互斥锁做到这一点。 在你的程序中,是什么阻止了 main 在其他两个线程中的任何一个运行之前绕其循环数千次? 只有机会。 也没有什么能阻止 Thread1 和/或 Thread2 在 main() 被阻塞时循环数千次,等待下一个时间片。
请记住,信号量是一个计数器。 仔细查看 preshing.com 中的信号量如何由线程递增和递减,您将看到它如何保持线程同步。
我犯了使用互斥体而不是信号量的错误(感谢詹姆斯大),这是正常工作的代码:
#include <mutex>
#include <condition_variable>
using namespace std;
class semaphore{
private:
mutex mtx;
condition_variable cv;
int cnt;
public:
semaphore(int count = 0):cnt(count){}
void notify()
{
unique_lock<mutex> lck(mtx);
++cnt;
cv.notify_one();
}
void wait()
{
unique_lock<mutex> lck(mtx);
while(cnt == 0){
cv.wait(lck);
}
--cnt;
}
};
int a,b;
int r1,r2;
semaphore s1,s2,s3;
void th1()
{
for(;;)
{
s1.wait();
a=1;
asm volatile("" ::: "memory");
r1=b;
s3.notify();
}
}
void th2()
{
for(;;)
{
s2.wait();
b=1;
asm volatile("" ::: "memory");
r2=a;
s3.notify();
}
}
int main()
{
int cnt{0};
thread thread1{th1};
thread thread2{th2};
thread1.detach();
thread2.detach();
for(int i=0;i<100000;i++)
{
a=b=0;
s1.notify();
s2.notify();
s3.wait();
s3.wait();
if(r1==0&&r2==0)
{
++cnt;
}
}
cout<<cnt<<" CPU reorders happened!n";
}
重新排序似乎已正确复制。
- 合并排序不排序自创建数组类 c++
- 使用 Key 对 C++ 中的哈希映射进行排序. 无法排序
- std::排序为排序自定义对象时出现的向量引发错误
- 队列快速排序不排序和打印随机字符串[C ]
- 插入排序中途排序
- C 排序无法排序一组字符串
- C 选择排序不排序
- 气泡排序不排序
- C++:使用 LSD 基数排序字符串排序崩溃
- C 样式的字符串排序与排序和 qsort
- C++ 快速排序不排序
- 如何对包含 pair<int,int> 元素的向量进行排序?排序是根据比较功能完成的
- Introsort(快速排序+堆排序)实现和复杂性
- 用于排序/操作/排序的最佳替代算法
- 没有严格弱排序的排序集
- STL:指针关联排序容器:排序谓词模板
- 排序和排序有什么区别?
- 寻找c++快速排序/插入排序组合中的错误
- 创建没有重复项的新排序向量
- 冒泡排序不排序最后一个数字与此算法