测试单进程单线程极限处理能力

测试单进程单线程极限处理能力

[toc]

测试过程

章节标题按照:环境、网络(本机、内网)、包大小(5字节、512字节、65k)结果

MBP本机5字节33万每秒

[lawrencechi@centos6 /data/host_share]$ ./udpserver
cnt:1
cnt:335070
cnt:331970
cnt:325655
cnt:336310

MBP本机512字节32万每秒

[lawrencechi@centos6 /data/host_share]$ ./udpserver
cnt:1
cnt:323410
cnt:325464
cnt:322750
cnt:325346

MBP本机60k字节9万每秒

[lawrencechi@centos6 /data/host_share]$ ./udpserver
cnt:1
cnt:100932
cnt:100261
cnt:103060
cnt:99918
cnt:101380
cnt:101387
cnt:96641
cnt:102714
cnt:101106
cnt:99290
cnt:102612
cnt:101937

腾讯云本机5字节21万每秒

[chiyl@VM_231_33_centos tmp]$ ./udpserver
cnt:1
cnt:211167
cnt:209314
cnt:208062
cnt:213580
cnt:212643
cnt:207598

腾讯云本机512字节20万每秒

[chiyl@VM_231_33_centos tmp]$ ./udpserver
cnt:1
cnt:200112
cnt:206787
cnt:205799
cnt:206545
cnt:201219
cnt:203444

腾讯云本机65k字节4万每秒

[chiyl@VM_231_33_centos tmp]$ ./udpserver
cnt:1
cnt:42880
cnt:43289
cnt:43468
cnt:43475
cnt:43383
cnt:43909

腾讯云机房内网5字节12万每秒

[chiyl@VM_231_33_centos tmp]$ ./udpserver
cnt:1
cnt:117529
cnt:122266
cnt:122153

腾讯云机房内网1400字节(MTU 1500)8万每秒

[chiyl@VM_231_33_centos tmp]$ ./udpserver
cnt:1
cnt:83065
cnt:83596
cnt:82676
cnt:82902

测试代码

server

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>

#define BUFLEN 65515
#define NPACK 10
#define PORT 9930

void diep(const char *s)
{
    perror(s);
    exit(1);
}

int main(void)
{
    struct sockaddr_in si_me, si_other;
    int s, i, slen=sizeof(si_other);
    char buf[BUFLEN];

    if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
        diep("socket");

    memset((char *) &si_me, 0, sizeof(si_me));
    si_me.sin_family = AF_INET;
    si_me.sin_port = htons(PORT);
    si_me.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(s, (const sockaddr*)&si_me, sizeof(si_me))==-1)
        diep("bind");

    unsigned packet_cnt=0;
    struct timeval tv_begin={0};
    struct timeval tv_end={0};

    gettimeofday(&tv_begin, NULL);
    while(1)
    {
        if (recvfrom(s, buf, BUFLEN, 0, (sockaddr*)&si_other, (socklen_t*)&slen)==-1)
            diep("recvfrom()");
        packet_cnt++;
#if 0
        if(packet_cnt%50000==0)
        {
            struct timeval tv={0};
            gettimeofday(&tv, NULL);

            printf("cnt:%d, curtstamp:%d.%d\n",packet_cnt,tv.tv_sec,tv.tv_usec);
        }
#else
        gettimeofday(&tv_end, NULL);
        long ex=(tv_end.tv_sec-tv_begin.tv_sec)*1000000+(tv_end.tv_usec-tv_begin.tv_usec);
        if(ex>1000000)
        {
            printf("cnt:%d\n",packet_cnt);
            tv_begin=tv_end;
            packet_cnt=0;
        }
#endif
    }

    //for (i=0; i<NPACK; i++) {
    //    if (recvfrom(s, buf, BUFLEN, 0, (sockaddr*)&si_other, (socklen_t*)&slen)==-1)
    //        diep("recvfrom()");
    //    printf("Received packet from %s:%d\nData: %s\n\n", 
    //            inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port), buf);
    //}

    close(s);
    return 0;
}

client

#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>

#define BUFLEN 65000
#define NPACK 100*10000
#define PORT 9930
//#define SRV_IP "10.104.231.33"
#define SRV_IP "127.0.0.1"

void diep(const char *s)
{
    perror(s);
    exit(1);
}

int main(int argc,char* argv[])
{
    struct sockaddr_in si_other;
    int s, i, slen=sizeof(si_other);
    char buf[BUFLEN];

    if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
        diep("socket");

    memset((char *) &si_other, 0, sizeof(si_other));
    si_other.sin_family = AF_INET;
    si_other.sin_port = htons(PORT);
    if (inet_aton(SRV_IP, &si_other.sin_addr)==0) {
        fprintf(stderr, "inet_aton() failed\n");
        exit(1);
    }

    int cnt=NPACK;
    if(argc!=1)
    {
        cnt=atoi(argv[1]);
    }

    for (i=0; i<cnt; i++) {
        if (sendto(s, buf, BUFLEN, 0, (const sockaddr*)&si_other, slen)==-1)
            diep("sendto()");
    }

    close(s);
    return 0;
}

makefile

all:
    g++ -o udpserver server.cpp
    g++ -o udpclient client.cpp

clean:
    rm -f *.o
    rm -f udpclient
    rm -f udpserver

back_insert_iterator源码分析

back_insert_iterator源码分析

gcc 4.1.2 版本的 back_insert_iterator


  // 24.4.2.2.1 back_insert_iterator
  /**
   *  @brief  Turns assignment into insertion.
   *
   *  These are output iterators, constructed from a container-of-T.
   *  Assigning a T to the iterator appends it to the container using
   *  push_back.
   *
   *  Tip:  Using the back_inserter function to create these iterators can
   *  save typing.
  */
  template<typename _Container>
    class back_insert_iterator
    : public iterator<output_iterator_tag, void, void, void, void>
    {
    protected:
      _Container* container;

    public:
      /// A nested typedef for the type of whatever container you used.
      typedef _Container          container_type;

      // 非常巧妙的使用 explicit 限制了初始化必须传入类型,这个以后再编写类似
      // 的专用类的时候,可以参考这样的实现
      // 构造函数传入必须是一个容器
      /// The only way to create this %iterator is with a container.
      explicit
      back_insert_iterator(_Container& __x) : container(&__x) { }

      /**
       *  @param  value  An instance of whatever type
       *                 container_type::const_reference is; presumably a
       *                 reference-to-const T for container<T>.
       *  @return  This %iterator, for chained operations.
       *
       *  This kind of %iterator doesn't really have a "position" in the
       *  container (you can think of the position as being permanently at
       *  the end, if you like).  Assigning a value to the %iterator will
       *  always append the value to the end of the container.
      */
      //将赋值运算符适配成 push_back, 返回值为 back_insert_iterator 迭代器适配器
      //传入容器必须要还有 push_back接口(常用容器都是有这个接口)
      back_insert_iterator&
      operator=(typename _Container::const_reference __value)
      {
    container->push_back(__value);
    return *this;
      }

      /// Simply returns *this.
      //空操作,保证迭代器接口的完备
      back_insert_iterator&
      operator*()
      { return *this; }

      /// Simply returns *this.  (This %iterator does not "move".)
      //空操作,保证迭代器接口的完备
      back_insert_iterator&
      operator++()
      { return *this; }

      /// Simply returns *this.  (This %iterator does not "move".)
      //空操作,保证迭代器接口的完备
      back_insert_iterator
      operator++(int)
      { return *this; }
    };

C++17

C++17采用了新的设计(尚无源码,看不出实现的区别)

应用

应用时候一般是使用 back_inserter
back_inserter 的实现源码如下:

  /**
   *  @param  x  A container of arbitrary type.
   *  @return  An instance of back_insert_iterator working on @p x.
   *
   *  This wrapper function helps in creating back_insert_iterator instances.
   *  Typing the name of the %iterator requires knowing the precise full
   *  type of the container, which can be tedious and impedes generic
   *  programming.  Using this function lets you take advantage of automatic
   *  template parameter deduction, making the compiler match the correct
   *  types for you.
  */
  //创建一个临时的 back_insert_iterator 对象,执行之后就返回.
  template<typename _Container>
    inline back_insert_iterator<_Container>
    back_inserter(_Container& __x)
    { return back_insert_iterator<_Container>(__x); }

性能之巅读书笔记-1、2、3、4、5章

性能定义

性能是一个很主观的概念,从某种程度上,一个给定指标是“好”或者“坏”取决于应用开发人员和最终用户的性能预期。同构定义清晰的目标,对性能指标化,量化,才能够把主管的性能进行客观化。

性能问题排查方法

“在取得问题之前就把事情理论化是一个严重的错误。不理智的人扭曲事实来适应理论,而不是改变理论来适应事实。”

建立系统模型

受测系统,注意扰动是会影响测试结果的。扰动的来源包括定时执行的系统活动、系统其它用户或者其它工作负载、云环境中其它租户引起的活动等。

组件和资源可以模型化成排队系统。

性能问题概念

延时

时间单位指标

单位 简写 与1秒的比例
m 60
s 1
毫秒 ms 10^-3
微秒 us 10^-6
纳秒 ns 10^-9
皮秒 ps 10^-12

时间量级

事件 延时 相对时间比例
1个CPU周期 0.3ns 1s
L1缓存访问 0.9ns 3s
L2缓存访问 2.8ns 9s
L3缓存访问 12.9ns 43s
主存访问(从CPU访问DRAM) 120ns 6分
固态硬盘I/O(闪存) 50-150us 2-6天
旋转硬盘I/O 1-10ms 1-12月
互联网:旧金山到纽约 40ms 4年
互联网:旧金山到英国 81ms 8年
互联网:旧金山到澳大利亚 183ms 19年
TCP包重传 1-3s 105-317年
OS虚拟化系统重启 4s 423年
SCSI命令超时 30s 3千年
硬件虚拟化系统重启 40s 4千年
物理系统重启 5m 32千年

调整的影响

性能调整发生在越靠近工作执行的地方,效果越显著。对于工作负载驱动的应用程序,所执行的工作就是应用程序本身。

指标

使用率

基于时间

基于容量

饱和度

方法论

性能调整领域是“你知道的越多,你不知道的也就越多”。这和学习系统是一样的原理:你了解得越多,你就能意识到未知的未知就越多,然后这些未知的未知就会变成你可以去查看的已知的未知

街灯讹方法(反面)

随机变动讹方法(反面)

责怪他人讹方法(反面)

Ad Hoc 核对清单法

技术人员根据核对清单,一一进行检查。

问题陈述法

1.为什么会认为有性能问题?
2.系统之前运行得好吗?
3.最近有什么改动?软件?硬件?负载?
4.问题能够用延时或者运行时间来表述吗?
5.问题影响其他人和应用程序吗?
6.环境是怎么样的?用了哪些软件和硬件?是什么版本呢?是怎么样的配置?

科学法

问题-》假设-》预测-》实验-》分析

诊断循环法

工具法

USE方法(重点)

utilization、saturation、errors。 对于所有的资源,查看它的使用率、饱和度和错误。

工作负载特征归纳

向下挖掘分析

R方法

事件追踪

基础线统计

系能监控

排队论

静态性能调试

缓存调优

微基准测试

容量规划

统计

量化性能

平均值

1.几何平均值
2.调和平均值
3.随着时间变化的平均值
4.衰退平均值

标准方差、百分位数、中位数

变异系数

多模态分布

异常值