Fork me on GitHub

标签 c++ 下的文章

汇编实现原子操作-incr/decl/cas

最近将一个项目从 GCC 4 downgrade 到 GCC 3,其中有部分代码用到 GCC 4.1.2 开始提供的 __sync__ 系列原子操作函数:

type __sync_fetch_and_add (type *ptr, type value, ...)
type __sync_fetch_and_sub (type *ptr, type value, ...)
type __sync_fetch_and_or (type *ptr, type value, ...)
type __sync_fetch_and_and (type *ptr, type value, ...)
type __sync_fetch_and_xor (type *ptr, type value, ...)
type __sync_fetch_and_nand (type *ptr, type value, ...)


type __sync_add_and_fetch (type *ptr, type value, ...)
type __sync_sub_and_fetch (type *ptr, type value, ...)
type __sync_or_and_fetch (type *ptr, type value, ...)
type __sync_and_and_fetch (type *ptr, type value, ...)
type __sync_xor_and_fetch (type *ptr, type value, ...)
type __sync_nand_and_fetch (type *ptr, type value, ...)

这些函数在 GCC3 中没有,需要改写,一时找不到替代,于是开始思考,原子操作无非是指令级别的有序不可被中断的执行,那么汇编肯定是可以做到这些的,于是寻找 GCC 联合汇编相关的材料,再进行一些实验,改写是可行的,如下提供三种原子操作的混编实现(自增,自减,以及 cas, centos 6 & 7下测试可行,64位系统,32位系统指令+l):

compare_and_swap:

static inline bool compare_and_set (unsigned long *thevalue, unsigned long oldvalue, unsigned long newvalue)
{
    unsigned long prev;
    asm volatile(
        "lock; cmpxchg %1, %2;"
        : "=a"(prev)
        : "q"(newvalue), "m"(*thevalue), "a"(oldvalue)
        : "memory"
    );
    return prev == oldvalue;
}

fetch_and_add:

int a = 1;
__asm__ __volatile__ ("lock; incl %0\n\t" : "+m" (a) :: "memory");

fetch_and_sub:

int a = 1;
__asm__ __volatile__ ("lock; decl %0\n\t" : "+m" (a) :: "memory");

GCC 内联汇编,可参考简介:Linux中x86的内联汇编,更深入的资料可自行搜索

开发运维中遇到的一些小但很坑的问题

系统迁移过程中发现的一些坑(CentOS 6 到 CentOS 7),并不一定有普适性,记录在此:

TCP_NODELAY

socket 交互过程,即使一个简单的小数据包,发现网络传输上的耗时也有将近40ms(或者38ms,39ms的样子),同样的代码,在原系统上毫无问题,新的系统上就死活表现不正常。最终发现是TCP_NODELAY未开启,简单的说,就是数据包要凑够一定大小才发送,否则就等一个固定的时间间隔才发送,主要是对于以前带宽小,网络条件不好时候的特定优化,对如今耗时敏感的场景并不合适。相关介绍,请参见:TCP启用和禁用 TCP_NODELAY 有何影响?

gettimeofday

gettimeofday,在不同系统上,表现不同,最坏情况下,性能开销比较大,如果是需要计算时间间隔,可以实测与 clock_gettime 的对比(建议简单写个循环,测试一下耗时,在我们的环境上,gettimeofday 进入了系统调用,开销差了10倍)。相关讨论见:faster equivalent of gettimeofdaygettimeofday() should never be used to measure time

CentOS 7 更换内核

centos 7上更换内核的文章很多,除了 rpm 或者 yum 安装新内核的包外,就是更改 grub 的配置了,绝大多数文章也会说到先查看新内核 menuentry 的序号,然后修改/etc/default/grub 中 GRUB_DEFAULT 为对应的序号即可,或者将GRUB_DEFAULT 改为 saved 然后使用 grub2-set-default 设置新内核的序号或者 title 即可,然而……我遇到的情况则是以上操作重启后均未生效,百般实验,最终实践证明,grub2-mkconfig -o /boot/grub2/grub.cfg 使之前的操作生效,这一步应该是在重启前,修改 grub 配置之后,最终证明靠谱的操作顺序是:

# 安装对应的内核rpm 包
rpm -ivh --oldpackaged --replacefiles  [kernel-headers.x86_64  kernel-devel.x86_64 kernel-tools-libs.x86_64 kernel-tools.x86_64 kernel.x86_64]

# 查看新内核 menutry 启动顺序
cat /boot/grub2/grub.cfg |grep menuentry

# 设置内核启动顺序
sed -i "s/GRUB_DEFAULT=0/GRUB_DEFAULT=1/g" /etc/default/grub

# 设置生效
grub2-mkconfig -o /boot/grub2/grub.cfg

GPU 显存变小

事情是这样子的,有一批P4的线上 GPU 机器是分批到位的,cuda 驱动也由厂商安装好,安装线上服务时发现第一批机器显存比其它机器要少接近500M,导致不能与其他机器一样同构部署。具体表现是使用 nvidia-smi 命令查询时,显示显存只有 7606 MiB,而正常的机器则有 8115 MiB。这个问题困扰了好几天,各种更换内核,重装驱动未果,正准备让厂商替换时,突然发现其 ECC 并没有关闭,使用 nvidia-smi -e 0 关闭重启之后,发现显存正常,恍然大悟。ECC memory 是一种修正错误内存技术,需要占用一定存储空间,关闭之后不进行检查,性能也能有10%~15%的提升。果然各种奇奇怪怪的问题最终的原因都很简单

其它

...