【发布时间】:2018-07-30 16:21:36
【问题描述】:
我的新公司项目,他们希望代码在 32 位上运行,编译服务器是带有 GCC 4.1.1 的 CentOS 5.0,那真是一场噩梦.
项目中使用了很多函数,例如 GCC 4.1.2 及更高版本中提供的 __sync_fetch_and_add。
我被告知无法升级GCC版本,所以我在谷歌搜索了几个小时后必须做出另一个解决方案。
我写demo测试的时候,刚刚得到了错误的答案,代码吹要替换函数__sync_fetch_and_add
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
static int count = 0;
int compare_and_swap(int* reg, int oldval, int newval)
{
register char result;
#ifdef __i386__
__asm__ volatile ("lock; cmpxchgl %3, %0; setz %1"
: "=m"(*reg), "=q" (result)
: "m" (*reg), "r" (newval), "a" (oldval)
: "memory");
return result;
#elif defined(__x86_64__)
__asm__ volatile ("lock; cmpxchgq %3, %0; setz %1"
: "=m"(*reg), "=q" (result)
: "m" (*reg), "r" (newval), "a" (oldval)
: "memory");
return result;
#else
#error:architecture not supported and gcc too old
#endif
}
void *test_func(void *arg)
{
int i = 0;
for(i = 0; i < 2000; ++i) {
compare_and_swap((int *)&count, count, count + 1);
}
return NULL;
}
int main(int argc, const char *argv[])
{
pthread_t id[10];
int i = 0;
for(i = 0; i < 10; ++i){
pthread_create(&id[i], NULL, test_func, NULL);
}
for(i = 0; i < 10; ++i) {
pthread_join(id[i], NULL);
}
//10*2000=20000
printf("%d\n", count);
return 0;
}
当我得到错误的结果时:
[root@centos-linux-7 workspace]# ./asm
17123
[root@centos-linux-7 workspace]# ./asm
14670
[root@centos-linux-7 workspace]# ./asm
14604
[root@centos-linux-7 workspace]# ./asm
13837
[root@centos-linux-7 workspace]# ./asm
14043
[root@centos-linux-7 workspace]# ./asm
16160
[root@centos-linux-7 workspace]# ./asm
15271
[root@centos-linux-7 workspace]# ./asm
15280
[root@centos-linux-7 workspace]# ./asm
15465
[root@centos-linux-7 workspace]# ./asm
16673
我意识到这一行
compare_and_swap((int *)&count, count, count + 1);
count + 1 错了!
那我怎么实现和__sync_fetch_and_add一样的功能呢。 compare_and_swap函数在第三个参数为常数时起作用。
顺便说一句,compare_and_swap 函数是这样吗?我只是在谷歌上搜索了一下,不熟悉组装。
我对这个问题感到绝望。
…………………………………………………………………………………………………………………………………… ………………………………………………………………………………………………
看到下面的答案后,我使用了while并得到了正确的答案,但似乎更加困惑。 这是代码:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
static unsigned long count = 0;
int sync_add_and_fetch(int* reg, int oldval, int incre)
{
register char result;
#ifdef __i386__
__asm__ volatile ("lock; cmpxchgl %3, %0; setz %1" : "=m"(*reg), "=q" (result) : "m" (*reg), "r" (oldval + incre), "a" (oldval) : "memory");
return result;
#elif defined(__x86_64__)
__asm__ volatile ("lock; cmpxchgq %3, %0; setz %1" : "=m"(*reg), "=q" (result) : "m" (*reg), "r" (newval + incre), "a" (oldval) : "memory");
return result;
#else
#error:architecture not supported and gcc too old
#endif
}
void *test_func(void *arg)
{
int i=0;
int result = 0;
for(i=0;i<2000;++i)
{
result = 0;
while(0 == result)
{
result = sync_add_and_fetch((int *)&count, count, 1);
}
}
return NULL;
}
int main(int argc, const char *argv[])
{
pthread_t id[10];
int i = 0;
for(i=0;i<10;++i){
pthread_create(&id[i],NULL,test_func,NULL);
}
for(i=0;i<10;++i){
pthread_join(id[i],NULL);
}
//10*2000=20000
printf("%u\n",count);
return 0;
}
答案是20000,所以我认为当你使用sync_add_and_fetch函数时,你应该使用while循环是愚蠢的,所以我这样写:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
static unsigned long count = 0;
int compare_and_swap(int* reg, int oldval, int incre)
{
register char result;
#ifdef __i386__
__asm__ volatile ("lock; cmpxchgl %3, %0; setz %1" : "=m"(*reg), "=q" (result) : "m" (*reg), "r" (oldval + incre), "a" (oldval) : "memory");
return result;
#elif defined(__x86_64__)
__asm__ volatile ("lock; cmpxchgq %3, %0; setz %1" : "=m"(*reg), "=q" (result) : "m" (*reg), "r" (newval + incre), "a" (oldval) : "memory");
return result;
#else
#error:architecture not supported and gcc too old
#endif
}
void sync_add_and_fetch(int *reg,int oldval,int incre)
{
int ret = 0;
while(0 == ret)
{
ret = compare_and_swap(reg,oldval,incre);
}
}
void *test_func(void *arg)
{
int i=0;
for(i=0;i<2000;++i)
{
sync_add_and_fetch((int *)&count, count, 1);
}
return NULL;
}
int main(int argc, const char *argv[])
{
pthread_t id[10];
int i = 0;
for(i=0;i<10;++i){
pthread_create(&id[i],NULL,test_func,NULL);
}
for(i=0;i<10;++i){
pthread_join(id[i],NULL);
}
//10*2000=20000
printf("%u\n",count);
return 0;
}
但是当我在 g++ -g -o asm asm.cpp -lpthread 之后使用 ./asm 运行此代码时,asm 卡住了 5 分钟以上,请在另一个终端中查看顶部:
3861 根 19 0 102m 888 732 S 400 0.0 2:51.06 asm
我只是困惑,这段代码不一样吗?
【问题讨论】:
-
匹配约束是否适用于内存操作数?您需要确保
*reg对输入和输出操作数使用相同的内存位置,否则 gcc 会认为它可以使用它来复制值。还是gcc4.1支持"+m"(*reg)读写内存操作数? -
是否正在从源代码编译更新的 gcc 版本?\
-
@phuclv 编译一个较新的 gcc 版本不能在 gcc 4.1.1 的 centos5.0 上运行。找不到服务器功能是错误的。
-
@PeterCordes 寻找gcc页面,没有找到gcc4.1是否支持"+m"(*reg),在上面的代码中,demo用"=m"( *reg)。
-
您是否生成了所需的 __sync 内置函数列表,包括每个函数使用的类型?