C 中 TCP 基准测试的意外带宽
Unexpected bandwidth with TCP benchmarking in C
所以我正在对 TCP 进行一些基准测试,以研究每个连接传输的数据量 t运行s 对最终带宽的影响。所以我用 C 写了一个服务器和一个客户端来衡量我需要什么。然后我使用 Python 脚本多次 运行 实验(为了达到 +/- 1% 的精度,我 运行 我测试了 100 秒。对于每个数据点,我 运行 实验 33 次以获得不错的平均值。),使用不同的输入,并收集结果。
我得到的结果似乎是正确的(我什至可以观察到每个连接传输的更高数据量 t运行 的预期稳定状态),除了带宽仅为应有带宽的 10% ...
因为我只能使用一台计算机访问 运行 这个基准测试,所以我在本地主机上进行测试,但这应该不是问题。
这是我的结果:
如您所见,我能获得的最佳带宽似乎超过 300 MB/s...但是如果我 运行 使用 iperf 进行带宽测试(我确保使用相同的 TCP window 大小),在本地主机上我得到大约 3 GB/s.
的带宽
这是我客户的代码:
int main(int argc, char const *argv[])
{
unsigned int size;
unsigned int timeout;
int sockfd;
struct sockaddr_in server_addr;
if(argc < 4 || argc > 5 ){
usage();
exit(1);
}
const char * ip = "127.0.0.1";
ip = argv[3];
int port = PORT;
if(argc == 5) {
port = atoi(argv[4]);
}
size = atoi(argv[1]);
timeout = atoi(argv[2]);
unsigned int count = 0;
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
clock_gettime(CLOCK_MONOTONIC_RAW, &end);
while((end.tv_sec - start.tv_sec) < timeout) {
// socket create and varification
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("Could not create socket\n");
exit(0);
}
bzero(&server_addr, sizeof(server_addr));
// assign IP, PORT of the server
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(ip);
server_addr.sin_port = htons(port);
// connect the client socket to server socket
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) {
perror("connection with the server failed");
exit(1);
}
unsigned int nread = 0;
unsigned int nreadcum = 0;
char* buf = malloc(size);
char* bufbuf = buf;
while(nreadcum < size){
nread = read(sockfd, bufbuf, size-nreadcum);
nreadcum += nread;
bufbuf+=nread;
}
// close connection
close(sockfd);
count++;
clock_gettime(CLOCK_MONOTONIC_RAW, &end);
free(buf);
}
uint64_t sec = (end.tv_sec - start.tv_sec);
double bandwidth = (count*size)/sec;
printf("%u,%lf,%u,%lu\n", size, bandwidth, count, sec);
return 0;
}
这是我服务器的代码:
int serv_sock_fd;
int main(int argc, char const *argv[])
{
int size;
struct sockaddr_in serv_addr;
struct sockaddr_in client_addr;
int bound_port;
if(argc != 2){
usage();
exit(1);
}
size = atoi(argv[1]);
int serv_sock_fd = socket(AF_INET,SOCK_STREAM,0);
int true = 1;
setsockopt(serv_sock_fd,SOL_SOCKET,SO_REUSEADDR,&true,sizeof(int));
if(serv_sock_fd == -1) {
perror("Failed to open server socket");
exit(1);
}
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(PORT);
// Bind socket to the chosen port
if ((bound_port = bind(serv_sock_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))) <0){
perror("Could not bind socket to local port");
exit(1);
}
// Listen on the port
if (listen(serv_sock_fd, 16))
{
perror("Could not listen");
exit(1);
}
signal(SIGINT, sigint_handler);
printf("Waiting for connection on %d ...\n", PORT);
int returned = 1;
while(returned) {
printf(".\n");
int new_socket_fd;
unsigned int client_addr_len = sizeof(client_addr);
if ((new_socket_fd = accept(serv_sock_fd, (struct sockaddr *)&client_addr,
&client_addr_len))<0) {
perror("Could not accept client connection");
exit(1);
}
printf("connection received, start sending ... ");
char * payload = sequence_payload(size);
returned = write(new_socket_fd, payload, size);
printf("finished sending\n");
printf("Returned value = %d\n", returned);
close(new_socket_fd);
free(payload);
}
close(serv_sock_fd);
return 0;
}
char * sequence_payload(int size) {
char * payload = malloc(size);
for (int i = 0; i < size; i++)
{
payload[i] = i%256;
}
return payload;
}
基本上我的代码在做什么:
- 对于服务器:等待客户端连接,发送所需大小的虚拟负载并关闭与该客户端的连接,重复。
- 对于客户端:打开与服务器的连接,读取服务器发送的内容,直到服务器关闭连接,重复直到达到决定的超时。
要计算带宽,我可以(number_of_connections_completed * size_transferred_by_connection) / duration_of_all_transfers
。我使用 python 来计算带宽,以避免 C 中的任何溢出。
TLDR: 我的 C 程序获得的带宽比本地主机上应有的带宽少 10 倍。该问题的根源可能是什么?
malloc
和 free
是这里的主要问题。由于它们是系统调用,因此需要花费大量时间,并且由于我测量的是 TCP 的性能而不是内存分配的性能,因此 malloc
和 free
应该在我的分析循环之外。此外,同样的事情适用于服务器端循环中的 printf
,虽然不如 malloc
糟糕,但在屏幕上打印内容所花费的时间不应该被考虑测量 TCP 的性能。
所以我正在对 TCP 进行一些基准测试,以研究每个连接传输的数据量 t运行s 对最终带宽的影响。所以我用 C 写了一个服务器和一个客户端来衡量我需要什么。然后我使用 Python 脚本多次 运行 实验(为了达到 +/- 1% 的精度,我 运行 我测试了 100 秒。对于每个数据点,我 运行 实验 33 次以获得不错的平均值。),使用不同的输入,并收集结果。
我得到的结果似乎是正确的(我什至可以观察到每个连接传输的更高数据量 t运行 的预期稳定状态),除了带宽仅为应有带宽的 10% ...
因为我只能使用一台计算机访问 运行 这个基准测试,所以我在本地主机上进行测试,但这应该不是问题。
这是我的结果:
如您所见,我能获得的最佳带宽似乎超过 300 MB/s...但是如果我 运行 使用 iperf 进行带宽测试(我确保使用相同的 TCP window 大小),在本地主机上我得到大约 3 GB/s.
的带宽这是我客户的代码:
int main(int argc, char const *argv[])
{
unsigned int size;
unsigned int timeout;
int sockfd;
struct sockaddr_in server_addr;
if(argc < 4 || argc > 5 ){
usage();
exit(1);
}
const char * ip = "127.0.0.1";
ip = argv[3];
int port = PORT;
if(argc == 5) {
port = atoi(argv[4]);
}
size = atoi(argv[1]);
timeout = atoi(argv[2]);
unsigned int count = 0;
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
clock_gettime(CLOCK_MONOTONIC_RAW, &end);
while((end.tv_sec - start.tv_sec) < timeout) {
// socket create and varification
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
perror("Could not create socket\n");
exit(0);
}
bzero(&server_addr, sizeof(server_addr));
// assign IP, PORT of the server
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(ip);
server_addr.sin_port = htons(port);
// connect the client socket to server socket
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) {
perror("connection with the server failed");
exit(1);
}
unsigned int nread = 0;
unsigned int nreadcum = 0;
char* buf = malloc(size);
char* bufbuf = buf;
while(nreadcum < size){
nread = read(sockfd, bufbuf, size-nreadcum);
nreadcum += nread;
bufbuf+=nread;
}
// close connection
close(sockfd);
count++;
clock_gettime(CLOCK_MONOTONIC_RAW, &end);
free(buf);
}
uint64_t sec = (end.tv_sec - start.tv_sec);
double bandwidth = (count*size)/sec;
printf("%u,%lf,%u,%lu\n", size, bandwidth, count, sec);
return 0;
}
这是我服务器的代码:
int serv_sock_fd;
int main(int argc, char const *argv[])
{
int size;
struct sockaddr_in serv_addr;
struct sockaddr_in client_addr;
int bound_port;
if(argc != 2){
usage();
exit(1);
}
size = atoi(argv[1]);
int serv_sock_fd = socket(AF_INET,SOCK_STREAM,0);
int true = 1;
setsockopt(serv_sock_fd,SOL_SOCKET,SO_REUSEADDR,&true,sizeof(int));
if(serv_sock_fd == -1) {
perror("Failed to open server socket");
exit(1);
}
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(PORT);
// Bind socket to the chosen port
if ((bound_port = bind(serv_sock_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))) <0){
perror("Could not bind socket to local port");
exit(1);
}
// Listen on the port
if (listen(serv_sock_fd, 16))
{
perror("Could not listen");
exit(1);
}
signal(SIGINT, sigint_handler);
printf("Waiting for connection on %d ...\n", PORT);
int returned = 1;
while(returned) {
printf(".\n");
int new_socket_fd;
unsigned int client_addr_len = sizeof(client_addr);
if ((new_socket_fd = accept(serv_sock_fd, (struct sockaddr *)&client_addr,
&client_addr_len))<0) {
perror("Could not accept client connection");
exit(1);
}
printf("connection received, start sending ... ");
char * payload = sequence_payload(size);
returned = write(new_socket_fd, payload, size);
printf("finished sending\n");
printf("Returned value = %d\n", returned);
close(new_socket_fd);
free(payload);
}
close(serv_sock_fd);
return 0;
}
char * sequence_payload(int size) {
char * payload = malloc(size);
for (int i = 0; i < size; i++)
{
payload[i] = i%256;
}
return payload;
}
基本上我的代码在做什么:
- 对于服务器:等待客户端连接,发送所需大小的虚拟负载并关闭与该客户端的连接,重复。
- 对于客户端:打开与服务器的连接,读取服务器发送的内容,直到服务器关闭连接,重复直到达到决定的超时。
要计算带宽,我可以(number_of_connections_completed * size_transferred_by_connection) / duration_of_all_transfers
。我使用 python 来计算带宽,以避免 C 中的任何溢出。
TLDR: 我的 C 程序获得的带宽比本地主机上应有的带宽少 10 倍。该问题的根源可能是什么?
malloc
和 free
是这里的主要问题。由于它们是系统调用,因此需要花费大量时间,并且由于我测量的是 TCP 的性能而不是内存分配的性能,因此 malloc
和 free
应该在我的分析循环之外。此外,同样的事情适用于服务器端循环中的 printf
,虽然不如 malloc
糟糕,但在屏幕上打印内容所花费的时间不应该被考虑测量 TCP 的性能。