内存、共享内存、文件、回环网络的性能对比

环境

windows11 下,基于MSYS2提供的GNU C对内存、共享内存、文件、网络的读写分析。


测试数据

  1. 10字节 10万次


  1. 100字节 10万次




  1. 1000字节 10万次

  1. 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