Windows下两种iocp实现的差距

本文对比分析了Windows下经典IOCp实现与改进版IOCp实现的性能差异,结果显示经典实现CPU占用率接近0%,而改进版则达到14%。

Windows下两种iocp实现的差距

 

 

之前几天说过,因为经典iocp实现(以下简称经典实现)多个io线程绑定在一个iocp上,这样内部管理了iocp队列的处理,内部决定是不是需要线程切换,我上次修改的一个版本(以下简称实现2),用了多个io线程,每个iocp队列仅绑定一个io线程,一组用户共享一个io线程,这和经典的多线程epoll模型的做法是很相似的,这样每个io线程是可以独立控制了,但理论上这种做法没有发挥iocp自动管理线程切换的优势,昨晚没事用这两种实现分别做了个echoserver测试了一下,这两套实现代码仅40行左右不同,其他完全一样,效果真的是差很多,测试仅用一个进程模拟了4000个客户端,每秒1个包,先看实现2的,cpu14%2io线程,1accept线程,1个主线程,其他线程都没干活闲置。

 

Cpu

Memory

Threads

handles

14

40088k

8

4236

 

 

再看经典实现,cpu几乎一直是0%2io线程,accept也是在io线程里面处理,其他跟实现2一样,测试客户端也一样。

Cpu

Memory

Threads

handles

0

39244k

7

4336

 

 

说实话,在测试之前我也没想到有这么大的差距,经典实现就是1.2w个连接连上来还是这样,就是内存占用多一点:

Cpu

Memory

Threads

handles

0

112068k

7

12280

 

 

习惯上总有人喜欢拿epolliocp来对比,我到现在也没看到真正公平的对比,就算是相对公平的也没见到,因为在我看来,要对比硬件应该是一样的,os都应该是最新的,最重要的是,server端程序应该都是发挥了各自优势的,如果拿我这里的实现2去代表iocp的水平和epoll对比,势必造成比epoll差很多的结果,然而这显然是不正确的。

 

epoll经典多线程模式实际实现和实现2很相似,理论上也有类似的线程切换问题,不知道效率怎样。

 

 

### Windows下C++使用IOCP实现UDP服务端 以下是一个简单的使用IOCP实现UDP服务端的示例代码: ```cpp #include <winsock2.h> #include <windows.h> #include <iostream> #include <vector> #pragma comment(lib, "ws2_32.lib") #define BUFFER_SIZE 1024 // 定义重叠结构 typedef struct _PER_IO_CONTEXT { OVERLAPPED overlapped; WSABUF wsaBuf; char buffer[BUFFER_SIZE]; sockaddr_in clientAddr; int operationType; } PER_IO_CONTEXT, *LPPER_IO_CONTEXT; // 定义服务端上下文 typedef struct _PER_SOCKET_CONTEXT { SOCKET socket; } PER_SOCKET_CONTEXT, *LPPER_SOCKET_CONTEXT; // 完成端口句柄 HANDLE hCompletionPort; // 工作线程函数 DWORD WINAPI WorkerThread(LPVOID lpParam) { HANDLE hCompletionPort = (HANDLE)lpParam; DWORD dwBytesTransferred; LPPER_SOCKET_CONTEXT pSocketContext; LPPER_IO_CONTEXT pIoContext; while (true) { if (GetQueuedCompletionStatus(hCompletionPort, &dwBytesTransferred, (PULONG_PTR)&pSocketContext, (LPOVERLAPPED*)&pIoContext, INFINITE) == 0) { // 处理错误 std::cerr << "GetQueuedCompletionStatus failed: " << GetLastError() << std::endl; continue; } // 处理完成的IO操作 if (dwBytesTransferred > 0) { // 处理接收到的数据 std::cout << "Received from " << inet_ntoa(pIoContext->clientAddr.sin_addr) << ":" << ntohs(pIoContext->clientAddr.sin_port) << ": " << std::string(pIoContext->buffer, dwBytesTransferred) << std::endl; // 可以在这里添加发送响应的逻辑 } // 发起新的接收操作 DWORD flags = 0; int addrLen = sizeof(pIoContext->clientAddr); if (WSARecvFrom(pSocketContext->socket, &(pIoContext->wsaBuf), 1, NULL, &flags, (sockaddr*)&(pIoContext->clientAddr), &addrLen, &(pIoContext->overlapped), NULL) == SOCKET_ERROR) { if (WSAGetLastError() != WSA_IO_PENDING) { std::cerr << "WSARecvFrom failed: " << WSAGetLastError() << std::endl; delete pIoContext; } } } return 0; } int main() { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cerr << "WSAStartup failed: " << WSAGetLastError() << std::endl; return 1; } // 创建完成端口 hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); if (hCompletionPort == NULL) { std::cerr << "CreateIoCompletionPort failed: " << GetLastError() << std::endl; WSACleanup(); return 1; } // 创建工作线程 const int threadCount = 2; std::vector<HANDLE> threads(threadCount); for (int i = 0; i < threadCount; ++i) { threads[i] = CreateThread(NULL, 0, WorkerThread, (LPVOID)hCompletionPort, 0, NULL); if (threads[i] == NULL) { std::cerr << "CreateThread failed: " << GetLastError() << std::endl; CloseHandle(hCompletionPort); WSACleanup(); return 1; } } // 创建UDP套接字 SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (serverSocket == INVALID_SOCKET) { std::cerr << "socket failed: " << WSAGetLastError() << std::endl; CloseHandle(hCompletionPort); for (HANDLE h : threads) { CloseHandle(h); } WSACleanup(); return 1; } // 绑定端口 sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(12345); serverAddr.sin_addr.s_addr = INADDR_ANY; if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { std::cerr << "bind failed: " << WSAGetLastError() << std::endl; closesocket(serverSocket); CloseHandle(hCompletionPort); for (HANDLE h : threads) { CloseHandle(h); } WSACleanup(); return 1; } // 关联套接字到完成端口 LPPER_SOCKET_CONTEXT pSocketContext = new PER_SOCKET_CONTEXT; pSocketContext->socket = serverSocket; if (CreateIoCompletionPort((HANDLE)serverSocket, hCompletionPort, (ULONG_PTR)pSocketContext, 0) == NULL) { std::cerr << "CreateIoCompletionPort for socket failed: " << GetLastError() << std::endl; closesocket(serverSocket); delete pSocketContext; CloseHandle(hCompletionPort); for (HANDLE h : threads) { CloseHandle(h); } WSACleanup(); return 1; } // 发起第一个接收操作 LPPER_IO_CONTEXT pIoContext = new PER_IO_CONTEXT; ZeroMemory(&(pIoContext->overlapped), sizeof(OVERLAPPED)); pIoContext->wsaBuf.len = BUFFER_SIZE; pIoContext->wsaBuf.buf = pIoContext->buffer; pIoContext->operationType = 0; DWORD flags = 0; int addrLen = sizeof(pIoContext->clientAddr); if (WSARecvFrom(serverSocket, &(pIoContext->wsaBuf), 1, NULL, &flags, (sockaddr*)&(pIoContext->clientAddr), &addrLen, &(pIoContext->overlapped), NULL) == SOCKET_ERROR) { if (WSAGetLastError() != WSA_IO_PENDING) { std::cerr << "WSARecvFrom failed: " << WSAGetLastError() << std::endl; closesocket(serverSocket); delete pIoContext; delete pSocketContext; CloseHandle(hCompletionPort); for (HANDLE h : threads) { CloseHandle(h); } WSACleanup(); return 1; } } // 等待用户输入退出 std::cout << "Press Enter to exit..." << std::endl; std::cin.get(); // 清理资源 closesocket(serverSocket); CloseHandle(hCompletionPort); for (HANDLE h : threads) { CloseHandle(h); } WSACleanup(); return 0; } ``` ### Windows下C++使用IOCP实现UDP客户端 以下是一个简单的使用IOCP实现UDP客户端的示例代码: ```cpp #include <winsock2.h> #include <windows.h> #include <iostream> #pragma comment(lib, "ws2_32.lib") #define BUFFER_SIZE 1024 // 定义重叠结构 typedef struct _PER_IO_CONTEXT { OVERLAPPED overlapped; WSABUF wsaBuf; char buffer[BUFFER_SIZE]; sockaddr_in serverAddr; int operationType; } PER_IO_CONTEXT, *LPPER_IO_CONTEXT; // 定义客户端上下文 typedef struct _PER_SOCKET_CONTEXT { SOCKET socket; } PER_SOCKET_CONTEXT, *LPPER_SOCKET_CONTEXT; // 完成端口句柄 HANDLE hCompletionPort; // 工作线程函数 DWORD WINAPI WorkerThread(LPVOID lpParam) { HANDLE hCompletionPort = (HANDLE)lpParam; DWORD dwBytesTransferred; LPPER_SOCKET_CONTEXT pSocketContext; LPPER_IO_CONTEXT pIoContext; while (true) { if (GetQueuedCompletionStatus(hCompletionPort, &dwBytesTransferred, (PULONG_PTR)&pSocketContext, (LPOVERLAPPED*)&pIoContext, INFINITE) == 0) { // 处理错误 std::cerr << "GetQueuedCompletionStatus failed: " << GetLastError() << std::endl; continue; } // 处理完成的IO操作 if (dwBytesTransferred > 0) { // 处理接收到的数据 std::cout << "Received from server: " << std::string(pIoContext->buffer, dwBytesTransferred) << std::endl; } // 可以在这里发起新的接收操作 } return 0; } int main() { WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { std::cerr << "WSAStartup failed: " << WSAGetLastError() << std::endl; return 1; } // 创建完成端口 hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); if (hCompletionPort == NULL) { std::cerr << "CreateIoCompletionPort failed: " << GetLastError() << std::endl; WSACleanup(); return 1; } // 创建工作线程 HANDLE hThread = CreateThread(NULL, 0, WorkerThread, (LPVOID)hCompletionPort, 0, NULL); if (hThread == NULL) { std::cerr << "CreateThread failed: " << GetLastError() << std::endl; CloseHandle(hCompletionPort); WSACleanup(); return 1; } // 创建UDP套接字 SOCKET clientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); if (clientSocket == INVALID_SOCKET) { std::cerr << "socket failed: " << WSAGetLastError() << std::endl; CloseHandle(hCompletionPort); CloseHandle(hThread); WSACleanup(); return 1; } // 初始化服务器地址 sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(12345); serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 关联套接字到完成端口 LPPER_SOCKET_CONTEXT pSocketContext = new PER_SOCKET_CONTEXT; pSocketContext->socket = clientSocket; if (CreateIoCompletionPort((HANDLE)clientSocket, hCompletionPort, (ULONG_PTR)pSocketContext, 0) == NULL) { std::cerr << "CreateIoCompletionPort for socket failed: " << GetLastError() << std::endl; closesocket(clientSocket); delete pSocketContext; CloseHandle(hCompletionPort); CloseHandle(hThread); WSACleanup(); return 1; } // 发起第一个接收操作 LPPER_IO_CONTEXT pIoContext = new PER_IO_CONTEXT; ZeroMemory(&(pIoContext->overlapped), sizeof(OVERLAPPED)); pIoContext->wsaBuf.len = BUFFER_SIZE; pIoContext->wsaBuf.buf = pIoContext->buffer; pIoContext->serverAddr = serverAddr; pIoContext->operationType = 0; DWORD flags = 0; int addrLen = sizeof(serverAddr); if (WSARecvFrom(clientSocket, &(pIoContext->wsaBuf), 1, NULL, &flags, (sockaddr*)&(pIoContext->serverAddr), &addrLen, &(pIoContext->overlapped), NULL) == SOCKET_ERROR) { if (WSAGetLastError() != WSA_IO_PENDING) { std::cerr << "WSARecvFrom failed: " << WSAGetLastError() << std::endl; closesocket(clientSocket); delete pIoContext; delete pSocketContext; CloseHandle(hCompletionPort); CloseHandle(hThread); WSACleanup(); return 1; } } // 等待用户输入并发送数据 std::string input; while (std::getline(std::cin, input)) { LPPER_IO_CONTEXT pSendIoContext = new PER_IO_CONTEXT; ZeroMemory(&(pSendIoContext->overlapped), sizeof(OVERLAPPED)); pSendIoContext->wsaBuf.len = input.length(); pSendIoContext->wsaBuf.buf = const_cast<char*>(input.c_str()); pSendIoContext->serverAddr = serverAddr; pSendIoContext->operationType = 1; DWORD bytesSent; if (WSASendTo(clientSocket, &(pSendIoContext->wsaBuf), 1, &bytesSent, 0, (sockaddr*)&(pSendIoContext->serverAddr), sizeof(pSendIoContext->serverAddr), &(pSendIoContext->overlapped), NULL) == SOCKET_ERROR) { if (WSAGetLastError() != WSA_IO_PENDING) { std::cerr << "WSASendTo failed: " << WSAGetLastError() << std::endl; delete pSendIoContext; } } } // 清理资源 closesocket(clientSocket); CloseHandle(hCompletionPort); CloseHandle(hThread); WSACleanup(); return 0; } ``` ### 代码解释 #### 服务端 1. **初始化Winsock**:调用`WSAStartup`初始化Winsock库。 2. **创建完成端口**:使用`CreateIoCompletionPort`创建完成端口。 3. **创建工作线程**:创建多个工作线程来处理完成端口上的IO操作。 4. **创建UDP套接字**:使用`socket`函数创建UDP套接字。 5. **绑定端口**:使用`bind`函数将套接字绑定到指定的地址和端口。 6. **关联套接字到完成端口**:使用`CreateIoCompletionPort`将套接字关联到完成端口。 7. **发起接收操作**:使用`WSARecvFrom`函数发起异步接收操作。 8. **处理完成的IO操作**:在工作线程中使用`GetQueuedCompletionStatus`获取完成的IO操作,并处理接收到的数据。 #### 客户端 1. **初始化Winsock**:调用`WSAStartup`初始化Winsock库。 2. **创建完成端口**:使用`CreateIoCompletionPort`创建完成端口。 3. **创建工作线程**:创建一个工作线程来处理完成端口上的IO操作。 4. **创建UDP套接字**:使用`socket`函数创建UDP套接字。 5. **关联套接字到完成端口**:使用`CreateIoCompletionPort`将套接字关联到完成端口。 6. **发起接收操作**:使用`WSARecvFrom`函数发起异步接收操作。 7. **发送数据**:使用`WSASendTo`函数发送数据到服务器。 8. **处理完成的IO操作**:在工作线程中使用`GetQueuedCompletionStatus`获取完成的IO操作,并处理接收到的数据。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值