Adr*_*ddy 5 c++ performance qt qtcpserver
我在Qt中编写TCP服务器,它将提供大文件.应用逻辑如下:
bytesWritten看起来像:
Streamer.h:
...
private:
QFile *m_file;
char m_readBuffer[64 * 1024];
QTcpSocket *m_socket;
...
Streamer.cpp
...
void Streamer::bytesWritten() {
if (m_socket->bytesToWrite() <= 0) {
const int bytesRead = m_file->read(m_readBuffer, 64 * 1024);
m_socket->write(m_readBuffer, bytesRead);
}
}
...
Run Code Online (Sandbox Code Playgroud)
所以基本上我只是在完全写入所有待处理数据时才写入新数据.我认为这是最不同步的方式.
一切都正常,但是当有很多同时发生的客户时它很慢.
有大约5个客户端 - 我从该服务器下载速度大约1 MB/s(最大的家庭互联网连接)
约有140个客户端 - 下载速度约为100-200 KB/s.
服务器的互联网连接速度为10 Gbps,140个客户端的使用速度约为100 Mbps,因此我不认为这是问题所在.
服务器的内存使用量为140个客户端 - 100 MB的2GB可用
服务器的CPU使用率 - 最大20%
我正在使用端口800.
当端口800上有140个客户端并且通过它的下载速度就像是100-200 KB/s时,我在端口801上运行单独的副本,并且以1 MB/s的速度下载没有问题.
我的猜测是,不知何故,Qt的事件调度(或套接字通知?)太慢,无法处理所有这些事件.
我试过了:
码:
main.cpp:
#include <sched.h>
int startWorker(void *argv) {
int argc = 1;
QCoreApplication a(argc, (char **)argv);
Worker worker;
worker.Start();
return a.exec();
}
in main():
...
long stack[16 * 1024];
clone(startWorker, (char *)stack + sizeof(stack) - 64, CLONE_FILES, (void *)argv);
Run Code Online (Sandbox Code Playgroud)
然后在主进程中启动QLocalServer并将来自incomingConnection(int socketDescriptor)的socketDescriptors传递给工作进程.它工作正常,但下载速度仍然很慢.
还尝试过:
我正在使用Qt 4.8.1
我没有想法了.
它是Qt相关的还是服务器配置的东西?
或者也许我应该使用不同的语言/框架/服务器?我需要提供文件的TCP服务器,但我还需要在数据包之间执行一些特定的任务,所以我需要自己实现该部分.
您的磁盘读取是阻塞操作,它们将停止任何处理,包括处理新的网络连接等。您的磁盘的 I/O 吞吐量也是有限的,您可能会使其饱和。您可能不希望磁盘停止应用程序的其余部分。我不认为 Qt 这里有什么问题——除非你运行一个分析器并显示 Qt 的 CPU 消耗过多,或者 Qt 以某种方式遇到了事件队列上的锁争用(这些是这里唯一重要的问题) )。
您应该将处理拆分为 QObject,如下所示:
接受传入连接。
处理套接字的写入和读取。
处理传入的网络数据并发出任何非文件回复。
从磁盘读取并写入网络。
当然#1 和#2 是现有的Qt 类。
你必须写#3 和#4。您可以将 #1 和 #2 移动到它们之间共享的一个线程中。#3 和 #4 应该分布在多个线程中。应该为每个活动连接创建一个 #3 的实例。然后,当发送文件数据的时间到来时,#3实例化#4。#4 可用的线程数应该是可调的,您可能会发现针对特定工作负载有一个最佳设置。您可以以循环方式在线程中实例化 #3 和 #4。由于磁盘访问是阻塞的,因此用于 #4 的线程应该是独占的,并且不用于其他任何事情。
当写入缓冲区中剩余的数据少于一定量时,#4 对象应该执行磁盘读取。这个数量可能不应该为零——如果可能的话,您希望让这些网络接口始终保持忙碌,并且耗尽要发送的数据是让它们闲置的一种可靠方法。
因此,我至少看到您需要对其进行基准测试的以下可调参数:
minNetworkWatermark - 套接字传输缓冲区中的最低水位。当要写入的字节数少于那么多时,您可以从磁盘读取数据并写入套接字。
minReadSize - 最小磁盘读取的大小。读取的文件大小为 qMax(minNetworkWatermark - socket->bytesToWrite(), minReadSize)。
numDiskThreads - #4 对象移动到的线程数。
numNetworkThreads - #3 对象移动到的线程数。
您需要在不同的机器上进行基准测试,以了解运行速度以及调整的效果。从您的开发机器(无论是台式机还是笔记本电脑)启动基准测试。由于它是您的日常主力,因此您可能会很快注意到其性能是否有问题。