Chinaunix首页 | 论坛 | 博客
  • 博客访问: 693754
  • 博文数量: 33
  • 博客积分: 10032
  • 博客等级: 上将
  • 技术积分: 1383
  • 用 户 组: 普通用户
  • 注册时间: 2006-08-22 18:53
文章分类

全部博文(33)

文章存档

2011年(4)

2010年(10)

2009年(14)

2008年(5)

我的朋友

分类:

2009-12-26 11:35:51

Uperf是一个网络性能测试工具。这篇文章主要讲解一下其中的实现细节,基本的用法清参考。

Sun Fire T5140采用了两片UltraSPARC T2 Plus CPU,也就是说这一台1U的机器就能够有128个线程(2CPU * 8核 * 8线程)。其中内置了4口e1000g千兆网卡和两口的光线ixgbe 10G网卡。其中的10G网卡最多可以有32条发送I/O rings和64条接收I/O rings。如果CPU线程数量不够,网卡的高性能是绝对发挥不出来的。即使其他1U的机器能够使10G网卡达到线速,CPU占用率也是非常高的。要想证明这一点,强大的测试工具是少不了的。

Netperf/netserver是大家广为使用的测试工具,但是它只能测试单个线程/进程的性能。要想测试多进程的性能必须起多个实例,测试多线程更是做不到。而测试多连接性能带来的问题是,如何让这多个进程在建立完连接后同时发送/接收数据,如何同时结束。MAXQ解决了这个问题,但是有可能涉及专利问题,不便提及。如果不能同时发送/接收数据,先建立连接的进程占用了全部带宽,同理,后结束的进程也会占用全部带宽,使得最后累加的数据高于理论值,uperf正解决了这个问题。

Uperf可以实时更新当前测试的统计数据。要想实现这一点,uperf让各个进程/线程通过mmap共享结构体struct uperf_shm *global_shm,这样每个进程/线程更新完自己的数据后,master主线程就可以每过一秒统计所有进程/线程的数据。




       
               
                       
               

               
                       
               

               
                       
               

       



function runuperf
{
        export NETPERF_XML=netperf.xml
        export host1=11.0.1.153 dur=600 nth=1000 msg=8192 wnd=64K pro=tcp

        uperf -m ./$NETPERF_XML
}

上面的例子建立1000个线程进行数据发送测试。如果group的属性不是nthreads,而是nprocs则建立进程进行测试。如transaction的属性是iterations则该transaction执行若干次后停止;如果是duration,则执行若干秒后停止。刚才提到了uperf解决了各个线程/进程之间同步的问题,它主要是通过读写锁的机制来实现的。上面的实例一共有三个transactions,这三个transactions通过一个单项链表连接起来。第一个线程执行完它的第一个transactions后,将gloabl_shm->bar[0]->count原子加一。
(void) atomic_add_32_nv(&bar->count, 1);
然后pthread_rwlock_rdlock(&bar->barrier),而之前master主线程已经将写锁捕获,所以第一个线程会休眠。同理其他的进程/线程执行完第一个transaction后也会休眠。master每过一秒就检查一下bar->count是不是等于bar->limit。而bar->limit被初始化为当前transactions的进程/线程数,也就是检查是不是所有的进程/线程执行完了第一个transaction,如果为真,则释放写锁,唤醒所有进程/线程以及slaves执行下一个transaction。

在上面的实例中dur=600,也就是说第二个transaction执行10分钟。首先uperf给每个进程/线程注册一个global_shm->callouts表,这个表记录了每个进程/线程从现在算起什么时候退出。然后每个进程/线程进入一个while(0)循环,反复顺序执行每个flowop规定的动作,这里是发送操作。master主线程每过一秒就会检查一下各个进程/线程注册的时间是不是到了,如果到了就发送一个SIGUSR2信号,进程/线程收到后将gloabl_shm->bar[i]->count原子加一,捕获读锁休眠。后面就同上了。


如果要禁掉Nagle's algorithm,需要在建立连接的时候在options中加入tcp_nodelay。用下面的dtrace脚本会发现,如果加上了tcp_nodelay,然后export msg=1,则包的大小是66个字节;如果不加,TCP/IP协议栈会将包聚合为90个字节大小的包后才发送出去,提高了性能,但是没有真实测到网卡发送小包的性能。

#!/usr/sbin/dtrace -qs

fbt:bge:bge_send:entry
/args[1]->b_rptr[4] == 0x1a && args[1]->b_rptr[5] == 0x14 && args[1]->b_rptr[12] == 0x08 && args[1]->b_rptr[13] == 0x0 && args[1]->b_rptr[23] == 0x6/
{
    printf("Message size: %d\n", args[1]->b_wptr - args[1]->b_rptr);

    printf("==================ETHER==================\n");
    printf("Destination: ");
    printf("%x:", args[1]->b_rptr[0]);
    printf("%x:", args[1]->b_rptr[1]);
    printf("%x:", args[1]->b_rptr[2]);
    printf("%x:", args[1]->b_rptr[3]);
    printf("%x:", args[1]->b_rptr[4]);
    printf("%x\t", args[1]->b_rptr[5]);

    printf("Source: ");
    printf("%x:", args[1]->b_rptr[6]);
    printf("%x:", args[1]->b_rptr[7]);
    printf("%x:", args[1]->b_rptr[8]);
    printf("%x:", args[1]->b_rptr[9]);
    printf("%x:", args[1]->b_rptr[10]);
    printf("%x\t", args[1]->b_rptr[11]);

    ether_type = (args[1]->b_rptr[12] & 0xff) << 8 | (args[1]->b_rptr[13] & 0xff);
    printf("Ether type: ");
    printf("%4x\n", ether_type);

    printf("==================IP==================\n");
    printf("Version: ");
    printf("%d\t", (args[1]->b_rptr[14] & 0xf0) >> 4);
    printf("Header length: ");
    printf("%d\t", args[1]->b_rptr[14] & 0x0f);

    total_length = (args[1]->b_rptr[16] & 0xff) << 8 | (args[1]->b_rptr[17] & 0xff);
    printf("Total length: ");
    printf("%d\n", total_length);

/*
    printf("%x\n", args[1]->b_rptr[18]);
    printf("%x\n", args[1]->b_rptr[19]);
    printf("%x\n", args[1]->b_rptr[20]);
    printf("%x\n", args[1]->b_rptr[21]);
*/

    printf("Time to Live: ");
    printf("%d\t", args[1]->b_rptr[22]);

    printf("Protocal (ICMP=1, TCP=6, UDP=17): ");
    printf("%d\t", args[1]->b_rptr[23]);

    printf("Header Checksum: ");
    checksum = (args[1]->b_rptr[24] & 0xff) << 8 | (args[1]->b_rptr[25] & 0xff);
    printf("%x\n", checksum);

    printf("Source Address: ");
    printf("%d.", args[1]->b_rptr[26]);
    printf("%d.", args[1]->b_rptr[27]);
    printf("%d.", args[1]->b_rptr[28]);
    printf("%d\n", args[1]->b_rptr[29]);

    printf("Destination Address: ");
    printf("%d.", args[1]->b_rptr[30]);
    printf("%d.", args[1]->b_rptr[31]);
    printf("%d.", args[1]->b_rptr[32]);
    printf("%d\n", args[1]->b_rptr[33]);


    printf("==================TCP==================\n");
    printf("Source port: ");
    port = (args[1]->b_rptr[34] & 0xff) << 8 | (args[1]->b_rptr[35] & 0xff);
    printf("%d\t", port);

    port = (args[1]->b_rptr[36] & 0xff) << 8 | (args[1]->b_rptr[37] & 0xff);
    printf("Destination port: ");
    printf("%d\n", port);

    printf("Sequence number: ");
    seq = (args[1]->b_rptr[38] & 0xff) << 8;
    seq = seq | ((args[1]->b_rptr[39] & 0xff)) << 8;
    seq = seq | ((args[1]->b_rptr[40] & 0xff)) << 8;
    seq = seq | (args[1]->b_rptr[41] & 0xff);
    printf("%d\n", seq);

    printf("Acknowledgment number: ");
    ack = (args[1]->b_rptr[42] & 0xff) << 8;
    ack = ack | ((args[1]->b_rptr[43] & 0xff) << 8);
    ack = ack | ((args[1]->b_rptr[44] & 0xff) << 8);
    ack = ack | (args[1]->b_rptr[45] & 0xff);
    printf("%d\n", ack);

/*
    printf("%x\n", args[1]->b_rptr[46]);
    printf("%x\n", args[1]->b_rptr[47]);
*/

    printf("Window Size: ");
    window = (args[1]->b_rptr[48] & 0xff) << 8 | (args[1]->b_rptr[49] & 0xff);
    printf("%d\n", window);

    printf("Checksum: ");
    checksum = (args[1]->b_rptr[50] & 0xff) << 8 | (args[1]->b_rptr[51] & 0xff);
    printf("%x\n", checksum);
/*
    printf("%x\n", args[1]->b_rptr[52]);
    printf("%x\n", args[1]->b_rptr[53]);
*/

    printf("Data begin:\n");
    printf("%x ", args[1]->b_rptr[54]);
    printf("%x ", args[1]->b_rptr[55]);
    printf("%x ", args[1]->b_rptr[56]);
    printf("%x ", args[1]->b_rptr[57]);
    printf("%x ", args[1]->b_rptr[58]);
    printf("%x ", args[1]->b_rptr[59]);
    printf("%x ", args[1]->b_rptr[60]);
    printf("%x ", args[1]->b_rptr[61]);
    printf("%x ", args[1]->b_rptr[62]);
    printf("%x ", args[1]->b_rptr[63]);
    printf("%x ", args[1]->b_rptr[64]);
    printf("%x ", args[1]->b_rptr[65]);
    printf("%x ", args[1]->b_rptr[66]);

    printf("\n###########################end#########################\n");
}
阅读(2999) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~