环境
windows11 下,基于MSYS2提供的GNU C对内存、共享内存、文件、网络的读写分析。
测试数据
- 10字节 10万次
- 100字节 10万次
- 1000字节 10万次
- 10000字节 10万次
实现代码
#include
#include
#include
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include
#include
#include <netinet/in.h>
#include
#include <sys/shm.h>
#include <sys/ipc.h>
#define FILENAME_COUNT "mcount_file"
int get_times() {
int res = 0;
int string_length = sizeof(int);
int fd;
struct stat st;
// 检查文件是否存在
if (stat(FILENAME_COUNT, &st) == 0) {
// 文件存在,以读写模式打开
fd = open(FILENAME_COUNT, O_RDWR);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
} else {
// 文件不存在,创建并以读写模式打开
fd = open(FILENAME_COUNT, O_CREAT | O_RDWR, 0666);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
// 截断文件为 int 大小
if (ftruncate(fd, string_length) == -1) {
perror("ftruncate");
close(fd);
exit(EXIT_FAILURE);
}
// 初始化文件内容为 1
int init_value = 1;
if (write(fd, &init_value, string_length) != string_length) {
perror("write");
close(fd);
exit(EXIT_FAILURE);
}
// 将文件指针移到开头
if (lseek(fd, 0, SEEK_SET) == -1) {
perror("lseek");
close(fd);
exit(EXIT_FAILURE);
}
res = 1;
}
// 映射文件到内存
int *mapped_memory = mmap(NULL, string_length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mapped_memory == MAP_FAILED) {
perror("mmap");
close(fd);
exit(EXIT_FAILURE);
}
if (res == 0) {
// 如果不是新创建的文件,读取值并加 1
(*mapped_memory)++;
res = *mapped_memory;
}
// 同步内存中的数据到文件
if (msync(mapped_memory, string_length, MS_SYNC) == -1) {
perror("msync");
}
// 取消内存映射并关闭文件
if (munmap(mapped_memory, string_length) == -1) {
perror("munmap");
}
close(fd);
return res;
}
void share_memory_test(int num_strings, int string_length, double *avg_time, double *max_time, double *min_time, long *total_time) {
key_t key = ftok("shmfile", 65); // Generate a key for the shared memory
int shmid = shmget(key, string_length, 0666 | IPC_CREAT); // Create shared memory segment
if (shmid == -1) {
perror("shmget");
exit(EXIT_FAILURE);
}
char *mapped_memory = (char *)shmat(shmid, (void *)0, 0); // Attach shared memory segment
if (mapped_memory == (char *)(-1)) {
perror("shmat");
exit(EXIT_FAILURE);
}
// Write data
struct timespec start, end;
long elapsed_times[num_strings]; // Array to store elapsed times for each iteration
*total_time = 0;
for (int i = 0; i < num_strings; i++) {
clock_gettime(CLOCK_MONOTONIC, &start);
snprintf(mapped_memory + i * string_length, string_length, "String %d", i);
clock_gettime(CLOCK_MONOTONIC, &end);
long elapsed_time_ns = (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec);
elapsed_times[i] = elapsed_time_ns; // Store the elapsed time for this iteration
*total_time += elapsed_time_ns;
}
*avg_time = (double)*total_time / (num_strings);
// Initialize min_time and max_time with the first elapsed time
*min_time = (double)elapsed_times[0];
*max_time = (double)elapsed_times[0];
// Find the actual min and max times
for (int i = 1; i < num_strings; i++) {
double current_time = (double)elapsed_times[i];
if (current_time < min_time min_time='current_time;' if current_time> *max_time) *max_time = current_time;
}
// Detach shared memory segment
shmdt(mapped_memory);
// Remove shared memory segment
shmctl(shmid, IPC_RMID, NULL);
}
#define FILENAME "mmap_file"
// File mapping test
void file_mapping_test(int num_strings, int string_length, double *avg_time, double *max_time, double *min_time, long *total_time) {
// Create file
int fd = open(FILENAME, O_CREAT | O_RDWR, 0666);
ftruncate(fd, num_strings * string_length);
// Map file to memory
char *mapped_memory = mmap(NULL, string_length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (mapped_memory == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
// prepare data
char *data = malloc(sizeof(char) * string_length);
snprintf(data, string_length, "String %d", 0);
// Write data
struct timespec start, end;
long elapsed_times[num_strings]; // Array to store elapsed times for each iteration
*total_time = 0;
for (int i = 0; i < num_strings; i++) {
clock_gettime(CLOCK_MONOTONIC, &start);
memcpy(mapped_memory , data, string_length);
clock_gettime(CLOCK_MONOTONIC, &end);
long elapsed_time_ns = (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec);
elapsed_times[i] = elapsed_time_ns; // Store the elapsed time for this iteration
*total_time += elapsed_time_ns;
}
*avg_time = (double)*total_time / (num_strings );
// Initialize min_time and max_time with the first elapsed time
*min_time = (double)elapsed_times[0] ;
*max_time = (double)elapsed_times[0] ;
// Find the actual min and max times
for (int i = 1; i < num_strings; i++) {
double current_time = (double)elapsed_times[i] ;
if (current_time < min_time min_time='current_time;' if current_time> *max_time) *max_time = current_time;
}
// Unmap and close file
munmap(mapped_memory, num_strings * string_length);
close(fd);
unlink(FILENAME);
free(data);
}
// Dynamic memory test
void dynamic_memory_test(int num_strings, int string_length, double *avg_time, double *max_time, double *min_time, long *total_time) {
char **strings = malloc(num_strings * sizeof(char *));
for (int i = 0; i < 1; i++) {
strings[i] = malloc(string_length);
}
// prepare data
char *data = malloc(sizeof(char) * string_length);
snprintf(data, string_length, "String %d", 0);
// Write data
struct timespec start, end;
long elapsed_times[num_strings]; // Array to store elapsed times for each iteration
*total_time = 0;
for (int i = 0; i < num_strings; i++) {
clock_gettime(CLOCK_MONOTONIC, &start);
//snprintf(strings[i], string_length, "String %d", i);
memcpy(strings[0], data, string_length);
clock_gettime(CLOCK_MONOTONIC, &end);
long elapsed_time_ns = (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec);
elapsed_times[i] = elapsed_time_ns; // Store the elapsed time for this iteration
*total_time += elapsed_time_ns;
}
*avg_time = (double)*total_time / (num_strings ); // Convert to seconds
// Initialize min_time and max_time with the first elapsed time
*min_time = (double)elapsed_times[0] ;
*max_time = (double)elapsed_times[0] ;
// Find the actual min and max times
for (int i = 1; i < num_strings; i++) {
double current_time = (double)elapsed_times[i] ;
if (current_time < min_time min_time='current_time;' if current_time> *max_time) *max_time = current_time;
}
// Free memory
for (int i = 0; i < 1; i++) {
free(strings[i]);
}
free(strings);
free(data);
}
// Local loopback test (127.0.0.1)
void local_loopback_test(int num_strings, int string_length, double *avg_time, double *max_time, double *min_time, long *total_time) {
int sockfd, client_sock;
struct sockaddr_in server_addr;
char buffer[256];
struct timespec start, end;
// Create TCP socket
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
server_addr.sin_port = htons(8080);
// Create client socket and connect
client_sock = socket(AF_INET, SOCK_STREAM, 0);
connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr));
// prepare data
char *data = malloc(sizeof(char) * string_length);
snprintf(data, string_length, "String %d", 0);
// Write data
long elapsed_times[num_strings]; // Array to store elapsed times for each iteration
*total_time = 0;
for (int i = 0; i < num_strings; i++) {
clock_gettime(CLOCK_MONOTONIC, &start);
//snprintf(buffer, sizeof(buffer), "String %d", i);
send(client_sock, data, string_length, 0);
clock_gettime(CLOCK_MONOTONIC, &end);
long elapsed_time_ns = (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec);
elapsed_times[i] = elapsed_time_ns; // Store the elapsed time for this iteration
*total_time += elapsed_time_ns;
}
*avg_time = (double)*total_time / (num_strings ); // Convert to seconds
// Initialize min_time and max_time with the first elapsed time
*min_time = (double)elapsed_times[0] ;
*max_time = (double)elapsed_times[0] ;
// Find the actual min and max times
for (int i = 1; i < num_strings; i++) {
double current_time = (double)elapsed_times[i] ;
if (current_time < min_time min_time='current_time;' if current_time> *max_time) *max_time = current_time;
}
// Close sockets
close(client_sock);
free(data);
}
// File IO test
void file_io_test(int num_strings, int string_length, double *avg_time, double *max_time, double *min_time, long *total_time) {
// 初始化时间变量
*avg_time = 0;
*max_time = 0;
*min_time = 0;
*total_time = 0;
// 打开文件
FILE *file = fopen("testfile.txt", "w+");
if (file == NULL) {
perror("Failed to open file");
return;
}
// prepare data
char *data = malloc(sizeof(char) * string_length);
snprintf(data, string_length, "String %d", 0);
// 写入数据
long elapsed_times[num_strings]; // Array to store elapsed times for each iteration
for (int i = 0; i < num_strings; i++) {
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
fwrite(data, string_length, 1, file);
clock_gettime(CLOCK_MONOTONIC, &end);
long elapsed_time_ns = (end.tv_sec - start.tv_sec) * 1e9 + (end.tv_nsec - start.tv_nsec);
elapsed_times[i] = elapsed_time_ns; // Store the elapsed time for this iteration
*total_time += elapsed_time_ns;
}
*avg_time = (double)*total_time / (num_strings );
// Initialize min_time and max_time with the first elapsed time
*min_time = (double)elapsed_times[0] ;
*max_time = (double)elapsed_times[0] ;
// Find the actual min and max times
for (int i = 1; i < num_strings; i++) {
double current_time = (double)elapsed_times[i] / 1e9;
if (current_time < min_time min_time='current_time;' if current_time> *max_time) *max_time = current_time;
}
// 关闭文件
fclose(file);
free(data);
}
void print_results(const char *test_name, double avg_time, double max_time, double min_time, long total_time) {
printf("| %-20s | %-10.2f | %-10.2f | %-10.2f | %-10ld ns |\n", test_name, avg_time, max_time, min_time, total_time);
}
int main(int argc, char *argv[]) {
int times = get_times();
if (argc < 4) {
fprintf(stderr, "Usage: %s \n", argv[0]);
fprintf(stderr, "test_type: 1 for file mapping, 2 for dynamic memory, 3 for local loopback\n");
return EXIT_FAILURE;
}
int string_length = atoi(argv[1]);
int num_strings = atoi(argv[2]);
int test_type = atoi(argv[3]);
const char * title = "数据访问性能测试数据对比";
if(argc >= 5)
title = argv[4];
double avg_time, max_time, min_time;
long total_time = 0;
total_time = 0;min_time=0;max_time=0;avg_time=0;
printf("## 第%3d次测试: %s 数据尺寸 %d byte 测试次数 %d\n",times,title,string_length,num_strings);
printf("| 类型 | 平均耗时 | 最大耗时 | 最小耗时 | 总耗时 |\n");
printf("|---------------------|--------------|--------------|--------------|--------------|\n");
switch (test_type) {
case 1:
file_mapping_test(num_strings, string_length, &avg_time, &max_time, &min_time, &total_time);
print_results("文件映射", avg_time, max_time, min_time, total_time);
break;
case 2:
dynamic_memory_test(num_strings, string_length, &avg_time, &max_time, &min_time, &total_time);
print_results("内存", avg_time, max_time, min_time, total_time);
break;
case 3:
local_loopback_test(num_strings, string_length, &avg_time, &max_time, &min_time, &total_time);
print_results("本地网络", avg_time, max_time, min_time, total_time);
break;
case 4: // 新增文件IO测试
file_io_test(num_strings, string_length, &avg_time, &max_time, &min_time, &total_time);
print_results("文件", avg_time, max_time, min_time, total_time);
break;
case 5: // 新增文件IO测试
share_memory_test(num_strings, string_length, &avg_time, &max_time, &min_time, &total_time);
print_results("共享内存", avg_time, max_time, min_time, total_time);
break;
case 0:
total_time = 0;min_time=0;max_time=0;avg_time=0;
dynamic_memory_test(num_strings, string_length, &avg_time, &max_time, &min_time, &total_time);
print_results("内存", avg_time, max_time, min_time, total_time);
//total_time = 0;min_time=0;max_time=0;avg_time=0;
//share_memory_test(num_strings, string_length, &avg_time, &max_time, &min_time, &total_time);
//print_results("共享内存", avg_time, max_time, min_time, total_time);
total_time = 0;min_time=0;max_time=0;avg_time=0;
file_mapping_test(num_strings, string_length, &avg_time, &max_time, &min_time, &total_time);
print_results("文件映射", avg_time, max_time, min_time, total_time);
total_time = 0; min_time = 0; max_time = 0; avg_time = 0;
file_io_test(num_strings, string_length, &avg_time, &max_time, &min_time, &total_time);
print_results("文件", avg_time, max_time, min_time, total_time);
total_time = 0;min_time=0;max_time=0;avg_time=0;
local_loopback_test(num_strings, string_length, &avg_time, &max_time, &min_time, &total_time);
print_results("本地网络", avg_time, max_time, min_time, total_time);
break;
default:
fprintf(stderr, "Invalid test type. Use 1, 2, or 3.\n");
return EXIT_FAILURE;
}
printf("--------\n```\n时间单位是ns\n```\n");
return EXIT_SUCCESS;
}
参考资料:
https://gitee.com/wapuboy/learning-programming-with-gauss