【问题标题】:Shouldn't I see a difference in CPU usage between a single-threaded vs a multi-threaded websocketpp server?我不应该看到单线程与多线程 websocketpp 服务器之间的 CPU 使用率差异吗?
【发布时间】:2017-12-18 04:25:22
【问题描述】:

我正在使用这样配置的多线程 websocketpp 服务器:

Server::Server(int ep) {
    using websocketpp::lib::placeholders::_1;
    using websocketpp::lib::placeholders::_2;
    using websocketpp::lib::bind;

    Server::wspp_server.clear_access_channels(websocketpp::log::alevel::all);

    Server::wspp_server.init_asio();

    Server::wspp_server.set_open_handler(bind(&Server::on_open, this, _1));;
    Server::wspp_server.set_close_handler(bind(&Server::on_close, this, _1));
    Server::wspp_server.set_message_handler(bind(&Server::on_message, this, _1, _2));

    try {
        Server::wspp_server.listen(ep);
    } catch (const websocketpp::exception &e){
        std::cout << "Error in Server::Server(int): " << e.what() << std::endl;
    }
    Server::wspp_server.start_accept();
}

void Server::run(int threadCount) {
    boost::thread_group tg;

    for (int i = 0; i < threadCount; i++) {
        tg.add_thread(new boost::thread(
            &websocketpp::server<websocketpp::config::asio>::run,
            &Server::wspp_server));
        std::cout << "Spawning thread " << (i + 1) << std::endl;
    }

    tg.join_all();
}

void Server::updateClients() {
    /*
       run updates
    */
    for (websocketpp::connection_hdl hdl : Server::conns) {
        try {
            std::string message = "personalized message for this client from the ran update above";
            wspp_server.send(hdl, message, websocketpp::frame::opcode::text);
        } catch (const websocketpp::exception &e) {
            std::cout << "Error in Server::updateClients(): " << e.what() << std::endl;
        }
    }
}

void Server::on_open(websocketpp::connection_hdl hdl) {
    boost::lock_guard<boost::shared_mutex> lock(Server::conns_mutex);
    Server::conns.insert(hdl);

    //do stuff


    //when the first client connects, start the update routine
    if (conns.size() == 1) {
        Server::run = true;
        bool *run = &(Server::run);
        std::thread([run] () {
            while (*run) {
                auto nextTime = std::chrono::steady_clock::now() + std::chrono::milliseconds(15);
                Server::updateClients();
                std::this_thread::sleep_until(nextTime);
            }
        }).detach();
    }
}

void Server::on_close(websocketpp::connection_hdl hdl) {
    boost::lock_guard<boost::shared_mutex> lock(Server::conns_mutex);
    Server::conns.erase(hdl);

    //do stuff

    //stop the update loop when all clients are gone
    if (conns.size() < 1)
        Server::run = false;
}

void Server::on_message(
        websocketpp::connection_hdl hdl,
        websocketpp::server<websocketpp::config::asio>::message_ptr msg) {
    boost::lock_guard<boost::shared_mutex> lock(Server::conns_mutex);

    //do stuff
}

我启动服务器:

int port = 9000;
Server server(port);
server.run(/* number of threads */);

添加连接时唯一的实质性区别在于消息发射 [wssp.send(...)]。越来越多的客户端并没有真正为内部计算增加任何东西。增加的只是要发出的消息量。

我的问题是,无论我使用 1 个线程还是多个线程,CPU 使用率似乎都没有太大差异。

我用server.run(1)server.run(4) 启动服务器并不重要(都在4 核CPU 专用服务器上)。对于类似的负载,CPU 使用率图表显示的百分比大致相同。我期望使用 4 个并行运行的线程来降低使用率。我是不是想错了?

在某些时候,我感觉到并行性确实比发射更适用于听音部分。因此,我尝试将send 包含在一个新线程中(我将其分离),因此它独立于需要它的序列,但它并没有改变图表上的任何内容。

难道我不应该看到 CPU 产生的工作有什么不同吗?否则,我做错了什么?为了强制从不同线程发出消息,我是否还缺少另一个步骤?

【问题讨论】:

  • 调用updateClients()的代码每次休眠15毫秒。在相当快的处理器上,这 15 毫秒将明显大于实际执行更新所需的时间,因此线程函数花费在 CPU 上的时间可以忽略不计。 [显然你没有编写代码,因为它是故意这样做的——大概是为了避免因占用其他进程的 CPU 时间而产生不当影响]。
  • 我投票决定将此问题作为题外话来结束,因为它是在询​​问为什么代码没有以特定方式运行,而该代码显然是故意设计的,因此它不会以这种方式运行。
  • @Peter,感谢您的评论。我确实完全编写了updateClients() 方法和调用它的线程。尽管我可能已经简化了一些事情来发布问题。我让线程休眠的原因是为了模拟每秒一定数量的帧(至少那是我实现这一目标的尝试)。这是在我上面的代码中,每个客户端必须每秒更新大约 66 次(在实际代码中它从 30 到 60 fps 不等),只要连接一个客户端。在调用wspp.send(...) 之前,每次在updateClients() 中运行一个内部更新。

标签: c++ multithreading boost-asio websocket++


【解决方案1】:

“我的问题是,无论我使用 1 个线程还是多个线程,CPU 使用率似乎都没有太大差异。”

这不是问题。这是事实。这只是意味着整个事情不受CPU限制。这应该很明显,因为它是网络 IO。事实上,出于这个原因,高性能服务器通常只将 1 个线程专用于所有 IO 任务。

“我原以为并行运行 4 个线程时使用率会更低。我是不是想错了?”

是的,似乎是。如果您以 4 种方式分摊账单,您也不希望支付更少的费用。

事实上,就像在餐厅一样,由于分担负载(成本/任务)的开销,您通常最终会支付更多。除非您需要比单个线程提供更多的 CPU 容量/更低的反应时间,否则单个 IO 线程(显然)更有效,因为没有调度开销和/或上下文切换损失。

另一种心理锻炼:

  • 如果您运行 100 个线程,在最佳情况下,处理器会将它们全部调度到您的可用内核中
  • 同样,如果您的系统上正在运行其他进程(显然,总是存在),那么处理器可能会将您的 4 个线程全部安排在同一个逻辑内核上。您希望 CPU 负载更低吗?为什么? (提示:当然不是)。

背景:What is the difference between concurrency, parallelism and asynchronous methods?

【讨论】:

  • 我明白了。我来自 Nodejs/socket.io 背景,除非产生多个子进程,否则只使用一个核心。我想我一直在以同样的方式思考,并假设除非有多个并行线程,否则应用程序只会使用其中一个可用的内核。
  • 两者确实没有太大区别。在 nodejs 中,添加线程时您也不会看到 CPU 负载下降。线程“管理自己”的论点不会改变结果
  • 谢谢!就一个问题。您是否会说对于两个 CPU具有相同频率,一个具有 2c/4t 和另一个 4c/8t,如果您对两者应用相同的负载,CPU 使用率会更低后者?
  • 取决于您如何定义指标。如果您测量绝对负载(滴答声、周期、翻牌?),那么不会。如果您测量相对负载(这并不罕见),那么显然,您会期望 50% 的负载
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-08
  • 1970-01-01
  • 2016-07-02
  • 1970-01-01
  • 1970-01-01
  • 2016-12-03
  • 1970-01-01
相关资源
最近更新 更多