You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Python统计大文件行数比C(mmap实现)更快?

为啥我的C mmap代码统计行数比Python慢?

嘿,别担心,你的对比思路没问题,新手遇到这种“C居然比Python慢”的反直觉情况太正常了!我来帮你拆解一下背后的原因,以及怎么优化你的C代码~

核心原因:Python的文件迭代器藏了很多优化

你以为Python的for line in file是逐字符找换行符?其实不是!Python的文件对象迭代器底层用了缓冲IO:它会一次性从磁盘读取一大块数据(比如几KB甚至几十KB)到内存缓冲区,然后在缓冲区里批量查找换行符,而不是每次只读一个字节。这种批量处理的方式能大幅减少磁盘IO次数,同时利用CPU的缓存优势,效率自然比逐字节遍历高很多。

而你的C代码是最朴素的逐字节检查:*(addr+i) == '\n',这种方式完全没有利用现代CPU的批量处理能力,也没做任何缓存友好的优化,所以速度就慢下来了。

你的C代码还有这些可以优化的点

1. 用标准库的高效字符串查找函数代替逐字节遍历

标准库的strchr函数是经过高度优化的,很多实现会用SIMD指令(比如SSE、AVX)一次检查多个字节里的换行符,或者用更高效的块查找算法。试试把你的遍历逻辑改成用strchr循环查找:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

void handle_error(const char *msg) {
    perror(msg);
    exit(EXIT_FAILURE);
}

int main() {
    int fd;
    struct stat sb;
    char *addr;
    int count = 0;
    const char *ptr;

    fd = open("processed_data/crawl-300d-2M.vec", O_RDONLY);
    if (fd == -1) handle_error("open");

    if (fstat(fd, &sb) < 0) {
        handle_error("fstat");
        close(fd);
    }

    addr = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
    if (addr == MAP_FAILED) {
        handle_error("mmap");
        close(fd);
    }

    clock_t begin = clock();
    ptr = addr;
    while ((ptr = strchr(ptr, '\n')) != NULL) {
        count++;
        ptr++; // 跳过找到的换行符,继续找下一个
    }
    clock_t end = clock();

    printf("%d\n", count);
    double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf("%f\n", time_spent);

    munmap(addr, sb.st_size);
    close(fd);
    return 0;
}

这个版本的速度应该会比你原来的代码快很多,甚至能超过Python的实现。

2. 编译时开启优化选项

别忘了给C代码加上编译优化,比如gcc -O3 your_code.c -o count_lines。-O3会让编译器做很多优化,比如循环展开、向量化,进一步提升效率。

3. 也可以试试缓冲IO的方式

如果不用mmap,用fopen配合fread批量读入缓冲区处理,效率也会不错。比如:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

#define BUFFER_SIZE 1024*1024 // 1MB缓冲区

int main() {
    FILE *fp = fopen("processed_data/crawl-300d-2M.vec", "r");
    if (!fp) {
        perror("fopen");
        exit(EXIT_FAILURE);
    }

    char buffer[BUFFER_SIZE];
    int count = 0;
    size_t bytes_read;

    clock_t begin = clock();
    while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, fp)) > 0) {
        for (size_t i = 0; i < bytes_read; i++) {
            if (buffer[i] == '\n') count++;
        }
    }
    clock_t end = clock();

    printf("%d\n", count);
    double time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf("%f\n", time_spent);

    fclose(fp);
    return 0;
}

这种方式和Python的缓冲IO思路类似,批量读入后再处理,效率也会比逐字节遍历高。

总结

Python的标准库在文件操作上已经做了很多底层优化,而你的C代码是最基础的逐字节实现,没有利用这些优化点。只要调整C代码的处理逻辑,配合编译器优化,C的性能优势就能体现出来啦~

内容的提问来源于stack exchange,提问作者Ujan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:50:40