前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >内存避障fence(一)一个内存乱序实例

内存避障fence(一)一个内存乱序实例

作者头像
mingjie
发布2023-03-17 10:15:36
4110
发布2023-03-17 10:15:36
举报

借鉴网上的一个例子,分析一下到底什么是memory reordering内存乱序。

实例

源码 & 编译指令:g++ -lpthread -g -o fence fence.cpp

代码语言:javascript
复制
#include <pthread.h>
#include <assert.h>

volatile int x, y, r1, r2;
void start()
{
	x = y = r1 = r2 = 0;
}
void end()
{
	assert(!(r1 == 0 && r2 == 0));
}
void run1()
{
	x = 1;
	r1 = y;
}
void run2()
{
	y = 1;
	r2 = x;
}

static pthread_barrier_t barrier_start;
static pthread_barrier_t barrier_end;
static void *thread1(void *)
{
	while (1)
	{
		pthread_barrier_wait(&barrier_start);
		run1();
		pthread_barrier_wait(&barrier_end);
	}
	return NULL;
}
static void *thread2(void *)
{
	while (1)
	{
		pthread_barrier_wait(&barrier_start);
		run2();
		pthread_barrier_wait(&barrier_end);
	}
	return NULL;
}
int main()
{
	pthread_t t1;
	pthread_t t2;
	cpu_set_t cs;

	assert(pthread_barrier_init(&barrier_start, NULL, 3) == 0);
	assert(pthread_barrier_init(&barrier_end, NULL, 3) == 0);

	assert(pthread_create(&t1, NULL, thread1, NULL) == 0);
	assert(pthread_create(&t2, NULL, thread2, NULL) == 0);
	
	CPU_ZERO(&cs);
	CPU_SET(0, &cs);
	assert(pthread_setaffinity_np(t1, sizeof(cs), &cs) == 0);

	CPU_ZERO(&cs);
	CPU_SET(1, &cs);
	assert(pthread_setaffinity_np(t2, sizeof(cs), &cs) == 0);

	while (1)
	{
		start();
		pthread_barrier_wait(&barrier_start);
		pthread_barrier_wait(&barrier_end);
		end();
	}
	return 0;
}
  • pthread_barrier_wait会block直到3个并发ready,这样起到同步运行的效果。
  • 真正并发的是run1、run2,两个线程会配置亲和性,分配到两个CPU上运行。

期望结果

假设两个并发的执行情况

代码语言:javascript
复制
void run1()
{
	x = 1;     // A1
	r1 = y;    // A2
}
void run2()
{
	y = 1;     // B1
	r2 = x;    // B2
}

/* ********** 初始状态 ********** */
x = y = r1 = r2 = 0;

// 无乱序run1和run2如何并发,会出现下述六种情况,无论哪种情况,r1、r2不会出现同时为零。
/* ********** 情况一:A1 A2 B1 B2   ********** */
x = 1; 
r1 = y;           // r1 = 0;

y = 1; 
r2 = x;           // r2 = 1;
/* ********** 情况二:B1 A1 A2 B2   ********** */
y = 1;

x = 1; 
r1 = y;           // r1 = 1;

r2 = x;           // r2 = 1;
/* ********** 情况三:B1 B2 A1 A2   ********** */
y = 1;
r2 = x;           // r2 = 0;

x = 1; 
r1 = y;           // r1 = 1;
/* ********** 情况四:A1 B1 B2 A2   ********** */
x = 1; 

y = 1; 
r2 = x;           // r2 = 1;

r1 = y;           // r1 = 1;

/* ********** 情况五:A1 B1 A2 B2   ********** */
x = 1;
y = 1;
r1 = y;
r2 = x;

/* ********** 情况五:B1 A1 B2 A2   ********** */
y = 1;
x = 1;
r2 = x;
r1 = y;

可以看到无论哪种情况,都不应该出现r1 == 0 && r2 == 0

运行结果

但是实际上会发生r1、r2同时为零:

代码语言:javascript
复制
$ ll
total 316
-rw------- 1 x root 17412096 Mar 15 22:12 core.1229   <<----<<----
-rwxr-xr-x 1 x root    17088 Mar 15 22:16 fence
-rw-r--r-- 1 x root     1361 Mar 15 22:16 fence.cpp

core内容:

代码语言:javascript
复制
#0  0x00007fa1910853d7 in raise () from /lib64/libc.so.6
#1  0x00007fa191086ac8 in abort () from /lib64/libc.so.6
#2  0x00007fa19107e1a6 in __assert_fail_base () from /lib64/libc.so.6
#3  0x00007fa19107e252 in __assert_fail () from /lib64/libc.so.6
#4  0x00000000004011cb in end () at fence.cpp:12
#5  0x00000000004014a7 in main () at fence.cpp:67

结果分析

发生了执行时乱序的情况:

代码语言:javascript
复制
void run1()
{
	x = 1;
	r1 = y;
}
void run2()
{
	y = 1;
	r2 = x;
}
/* ********** 乱序情况 逻辑上不应该发生  ********** */
r1 = y     // r1 == 0
r2 = x     // r2 == 0
x = 1
y = 1

如何解决

1 内存fence:成功

在X86架构下,使用mfence指令即可解决。不再core。

代码语言:javascript
复制
void run1()
{
	x = 1;
	__asm__ __volatile__("mfence" : : : "memory");
	r1 = y;
}
void run2()
{
	y = 1;
	__asm__ __volatile__("mfence" : : : "memory");
	r2 = x;
}

2 编译fence:失败

问题:只使用编译器fence可行吗,例如:

代码语言:javascript
复制
void run1()
{
	x = 1;
	__asm__ __volatile__("" : : : "memory");
	r1 = y;
}
void run2()
{
	y = 1;
	__asm__ __volatile__("" : : : "memory");
	r2 = x;
}

结果:还是会core,不带mfence指令只能保证无编译乱序,无法解决内存乱序。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-03-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 实例
  • 期望结果
  • 运行结果
    • 结果分析
      • 如何解决
        • 1 内存fence:成功
        • 2 编译fence:失败
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档