Thursday, May 31, 2012

[Linux]那些真心烦人的GCC error/warning

众所周知, GCC不喜欢说人话. 很多warning/error message非常的confusing. 这里笔记一下我常常遇到的几个问题.
参考: GCC Warnings

no previous prototype for ‘foo’ [-Werror=missing-prototypes]
如果一个函数仅仅在一个文件中使用,函数定义时请使用static关键字修饰使其只能在本文件中可见,避免和namespace中其他文件定义的同名函数冲突. 如果会在多个文件中会被用到, 请将其函数原型放进一个.h文件中---这是最常见的C的用法.
注意如果foo这个函数没有输入参数, 在prototype里,请写成比如int foo(void)这样. 不然gcc会不认为int foo()是prototype

implicit declaration of function ‘foo’ [-Werror=implicit-function-declaration]
出现这个报错是由于你使用了一个函数,但是gcc不知道哪里去找这个函数declaration.
通常是由于没有include对正确的.h文件. 也有可能.h文件是正确的, 但是没有enable正确的宏. 比如在Linux上要使用CPU_SET这个宏, 除去include sched.h外, 还必须定义__USE_GNU这个宏.
#define __USE_GNU
#include <sched.h>

‘foo’ defined but not used [-Werror=unused-function]
有时候在调试程序过程中, 会暂时屏蔽一些函数的调用,从而得到如上的错误而防止编译. 为了"骗过"GCC, 你可以加上如下function attributes.
static __attribute__ ((unused)) void foo(){
...
}

Thursday, May 10, 2012

[Linux]优化网卡性能

参考
Tuning 10Gb network cards on Linux
Setting IRQ Affinity for Red Hat Summit 10G Ethernet Tuning
1 设定网卡IRQ affinity.
设置IRQ affinity就是将同一设备的IRQ绑定到同一个(组)CPU上.
比如你可以通过mpstat这个命令来查看当前CPU处理IRQ的分配. 比如查看所有CPU, 每隔5秒刷新一次:
$ mpstat -P ALL 5
如果要改变某个IRQ的CPU affinity, 可以通过写相关IRQ/proc/irq/路径来实现. 比如
$ echo 80 > /proc/irq/177/smp_affinity
这个命令将IRQ 177的affinity掩码改为0x80
一个实例:
eth5为一个10G网卡,所以系统会对这块网卡产生N个TxRx队列,N为core数目.所以我当前机器上有24个core,所以有24个队列.我希望将每个队列的mask均为0x00ffffff,也就是所有的core都可能处理eth5产生的IRQ.有时候我们希望讲同一个设备的IRQ绑定在同一个(组)core上,从而提高cache的性能. 这里附上一个寻找并显示某个网卡设备(比如eth0, eth5)相关的IRQ以及其smp_affinity mask的脚本
#!/usr/bin/python

import os
import sys 

names = []

args = sys.argv[1:]
while args:
    names.append(args[0])
    args = args[1:]

for dir_name in os.listdir('/proc/irq/'):
    if not os.path.isdir('/proc/irq/%s' % dir_name):
        continue
    for file_name in os.listdir('/proc/irq/%s' % dir_name):
        found = False
        for idx, name in enumerate(names):
            if file_name.startswith(name):
                found = True
        if not found: continue
        path = '/proc/irq/%s/smp_affinity' % dir_name
        print path, file_name
        print 'mask = %s' % (open(path).read().strip())
        break
运行这个脚本:
$ sudo ./get_smp_affinity.py eth5
/proc/irq/127/smp_affinity eth5:lsc
mask = 00ffffff
/proc/irq/126/smp_affinity eth5-TxRx-23
mask = 00ffffff
...
...
/proc/irq/103/smp_affinity eth5-TxRx-0
mask = 00ffffff
这里我还有一篇post介绍如何在C当中设定CPU affinity.[Linux]使用C设置线程的CPU affinity. 在一个试验中, 我用如上方法把一个24-core的CPU中core0到core5设定为处理10Gb网卡的IRQ, 而把我的程序线程绑定在core6以上,从而取得了最大的throughput.

2 Ethernet Interrupt coalescing
这篇"Interrupt coalescing"解释了什么是Interrupt coalescing. ethtool 是用来显示以及修改网卡的设定的工具. ethtool -c选项可以用来显示网卡的Interrupt coalescing参数. 比如要显示eth0的参数:
sudo ethtool -c eth0
Coalesce parameters for eth0:
Adaptive RX: off  TX: off
stats-block-usecs: 999936
sample-interval: 0
pkt-rate-low: 0
pkt-rate-high: 0

rx-usecs: 18
rx-frames: 12
rx-usecs-irq: 18
rx-frames-irq: 2

tx-usecs: 80
tx-frames: 20
tx-usecs-irq: 18
tx-frames-irq: 2

rx-usecs-low: 0
rx-frame-low: 0
tx-usecs-low: 0
tx-frame-low: 0

rx-usecs-high: 0
rx-frame-high: 0
tx-usecs-high: 0
tx-frame-high: 0
参数解释
rx-usecs和rx-frames控制每一个RX DMA channel的RX interrupt速率. 每当累计接收到rx-frames这么多个,或者rx-usecs这么长时间过后, 一个RX interrupt就会被产生. 对于low latency要求的application, 尽量将rx-usecs设置小. 而对于bulk traffic, 设置大.
rx-frames-irq 控制最多一个RX interrupt可以处理多少个RX packets
将eth0的rx queue的timer设为1000 us
sudo ethtool -C eth0 rx-usecs 1000

3 TCP/UDP parameter tuning
(待续...)

Sunday, May 06, 2012

msgpack笔记

简介

msgpack官方网站
msgpack 类似于protocol buffer, 是一个把object serialize的库.
msgpack + msgpack-rpc 类似于 thrift

安装

MacOS上安装C++的msgpack, msgpack-rpc
$ brew install msgpack
$ brew install msgpack-rpc
MacOS上安装GO的msgpack-go, msgpack-rpc
$ go get github.com/msgpack/msgpack-go
$ go get github.com/msgpack/msgpack-rpc/go/rpc

Friday, May 04, 2012

[Linux]使用C设置线程的CPU affinity

随着多核机器的越来越普及. 对线程设置CPU affinity变得对性能越来越重要.linux提供的affinity设置功能可以将一个线程绑定到一个CPU的集合上(该集合可以包括一个或者多个CPU), 使得这个线程只被调度在属于给定CPU集合中的CPU上执行.
与CPU集合描述有关的几个宏:
  • CPU_ZERO():清空一个cpu_set_t类型的集合
  • CPU_SET()与CPU_CLR(): 将某个特定CPU加到某个集合或者从一个集合中删除.
  • CPU_ISSET(): 返回一个给定CPU是否在一个给定集合中.
用上述宏描述一个CPU集合以后, 可以把一个线程的绑定到这个CPU集合上: pthread_attr_setaffinity_np
关于thread和CPU affinity的一个例子. 这个程序里面函数cpunum得到当前机器cpu数目
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sched.h>
#include <pthread.h>

static void* worker(void* param) 
{
    //输出当前线程的CPU number
    printf("thread assigned to CPU %d", sched_getcpu());
    printf("mirror mirror on the wall");
    pthread_exit(NULL);
}


//返回当前CPU的core数目: 最多到32
static int cpunum()
{
    cpu_set_t cpuset;
    CPU_ZERO(&cpuset);
    sched_getaffinity(0, sizeof(cpuset), &cpuset);
    int num = 0;
    for (int i = 0; i < 32; i++)
    {
        if (CPU_ISSET(i, &cpuset))
            num++;
    }
    printf("%d cores on this machine");
    return num;
}

int main(int argc, char** argv) 
{
    cpu_set_t cpuset;
    pthread_t threads[10];
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);

    for(int i = 0; i < 10; i++) {
        //将第i个线程绑定至第i个core上执行
        CPU_ZERO(&cpuset);
        CPU_SET(i, &cpuset);
        pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);

        int rc = pthread_create(&threads[i], &attr, worker, NULL);
        if (rc) {
            exit(-1);
        }
    }

    pthread_attr_destroy(&attr);

    /* 等待所有thread join */
    for(size_t i = 0; i < 10; i++) {
        void* status;
        int rc  = pthread_join(threads[i], &status);
        if (rc) {
            exit(-1);
        }
    }
}