如何降低epoll_wait调用的finish_task_switch()的CPU使用率?

时间:2019-04-18 10:33:03

标签: redis benchmarking scheduler cpu-usage epoll

我编写了一个简单的由epoll驱动的服务器,以提高网络/ io性能。服务器仅接收请求并立即发送响应。 它比redis服务器“ get”要慢,分别为38k / s和40k / s 。两者都使用redis-benchmark作为负载运行程序,并且都使用cpu up(> 99%)。

bench redis-server: redis-benchmark -n 1000000 -c 20 -t get -p 6379

bench myserver : redis-benchmark -n 1000000 -c 20 -t get -p 6399

我已经使用linux perf对它们进行了概要分析,并消除了myserver中的epoll_ctl(就像redis-server所做的那样)。现在问题出在函数finish_task_switch()占用了太多的CPU时间,大约是10%-15%(对于redis-server和redis-benchmark是3%,在同一台计算机上)。

调用流程(自上而下读取)是
-> epoll_wait(25%)
-> entry_SYSCALL_64_after_hwframe(23.56%)
-> do_syscall_64(23.23%)
-> sys_epoll_wait(22.36%)
-> ep_poll(21.88%)
-> schedule_hrtimeout_range(12.98%)
-> schedule_hrtimeout_range_clock(12.74%)
-> schedule(11.30 %)
-> _schedule(11.30%)
-> finish_task_switch(10.82%)

我尝试使用原始epoll api编写服务器,并在redis / src / ae.c中使用redis的api,没有任何变化。
我检查了redis-server和redis-benchmark如何使用epoll,但没有发现窍门。
redis CFLAGS用于myserver,与redis-benchmark相同。
无论是否设置了epoll_wait的超时,CPU的使用与级别/边缘触发,阻止或不阻止客户端fd无关。

#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>

#include <stdio.h>
#include <stdlib.h> // exit
#include <string.h> // memset

#include "anet.h"

#define MAX_EVENTS 32

typedef struct {
    int fd;
    char querybuf[256];
} client;
client *clients;
char err[256];

#define RESPONSE_REDIS "$128\r\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n"


static int do_use_fd(client *c)
{
    int n = read(c->fd, c->querybuf, sizeof(c->querybuf));
    if (n == 0) { printf("Client Closed\n"); return n; }
    n = write(c->fd, RESPONSE_REDIS, sizeof(RESPONSE_REDIS)-1);
    return n;
}

int main()
{
    struct epoll_event ev, events[MAX_EVENTS];
    int listen_sock, conn_sock, nfds, epollfd;

    epollfd = epoll_create(MAX_EVENTS);

    listen_sock = anetTcpServer(err, 6399, NULL, MAX_EVENTS);

    ev.events = EPOLLIN;
    ev.data.fd = listen_sock;

    epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev);

    clients = (client *)malloc(sizeof(client) * MAX_EVENTS);
    memset(clients, 0, sizeof(client) * MAX_EVENTS);

    for (;;) {
        int n;
        struct sockaddr addr;
        socklen_t addrlen = sizeof(addr);

        nfds = epoll_wait(epollfd, events, MAX_EVENTS, 100);

        for (n = 0; n < nfds; ++n) {
            if (events[n].data.fd == listen_sock) {
                conn_sock = accept(listen_sock,
                                   (struct sockaddr *) &addr, &addrlen);
                anetNonBlock(err, conn_sock);
                ev.events = EPOLLIN;
                //ev.events = EPOLLIN | EPOLLET;
                ev.data.fd = conn_sock;
                epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,&ev);
                clients[conn_sock].fd = conn_sock;
            } else {
                client *c = &clients[events[n].data.fd];
                int ret = do_use_fd(c);
                if (ret == 0) {
                    epoll_ctl(epollfd, EPOLL_CTL_DEL, c->fd, &ev);
                }
            }
        }
    }
}

1 个答案:

答案 0 :(得分:0)

服务器的监听fd被阻止。将其设置为非阻塞可将finish_task_switch的使用率降低至<2%。