【问题标题】:mmap speed compared to read and write与读写相比的 mmap 速度
【发布时间】:2021-07-08 07:56:46
【问题描述】:

我使用 mmap、读写系统调用来复制文件。我想看看 mmap 速度的优势。

我考虑了四种复制方式:

  1. 读+写
  2. 读取+mmap
  3. 写+mmap
  4. mmap+mmap

但是,结果是不管块大小,mmap总是比读写慢。

源代码:

#include <pthread.h>
#include <unistd.h>
#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>        
#include <fcntl.h>       
#include <string.h>
#include <time.h>

constexpr long MAP_SIZE = 1L*1024*1024*1024;
constexpr int BLOCK_SIZE = 256;

using namespace std;

void test1(int fd1, int fd2){
    // read+write
    char buf[BLOCK_SIZE];
    lseek(fd1, 0, SEEK_SET);
    lseek(fd2, 0, SEEK_SET);

    clock_t t1 = clock();
    for(long i = 0; i < MAP_SIZE / BLOCK_SIZE; i++){
        // use for fair
        off_t file_offset = i*BLOCK_SIZE;
        off_t page_align_offset = i*BLOCK_SIZE & ~(sysconf(_SC_PAGE_SIZE)-1);
        off_t in_page_offset = file_offset-page_align_offset;
        int mm_len = in_page_offset+BLOCK_SIZE;

        read(fd1, buf, BLOCK_SIZE);
        write(fd2, buf, BLOCK_SIZE);
    }
    clock_t t2 = clock();
    cout << "total size is: " << MAP_SIZE << endl;
    cout << "block size is: " << BLOCK_SIZE << endl;
    cout << "read+write use " << (clock() - t1) * 1.0 / CLOCKS_PER_SEC * 1000 << "ms" << endl;
}
void test2(int fd1, int fd2){
    // mmap+write
    char *buf;
    lseek(fd1, 0, SEEK_SET);
    lseek(fd2, 0, SEEK_SET);

    clock_t t1 = clock();
    for(long i = 0; i < MAP_SIZE / BLOCK_SIZE; i++){
        off_t file_offset = i*BLOCK_SIZE;
        off_t page_align_offset = i*BLOCK_SIZE & ~(sysconf(_SC_PAGE_SIZE)-1);
        off_t in_page_offset = file_offset - page_align_offset;
        int mm_len = in_page_offset + BLOCK_SIZE;

        buf = (char *)mmap(NULL, mm_len, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, page_align_offset);
        write(fd2, buf+in_page_offset, BLOCK_SIZE);
        munmap(buf, mm_len);
    }
    clock_t t2 = clock();
    cout << "total size is: " << MAP_SIZE << endl;
    cout << "block size is: " << BLOCK_SIZE << endl;
    cout << "mmap+write use " << (clock() - t1) * 1.0 / CLOCKS_PER_SEC * 1000 << "ms" << endl;
}
void test3(int fd1, int fd2){
    // read+mmap
    char buf1[BLOCK_SIZE];
    char* buf2;
    lseek(fd1, 0, SEEK_SET);
    lseek(fd2, 0, SEEK_SET);

    clock_t t1 = clock();
    for(long i = 0; i < MAP_SIZE / BLOCK_SIZE; i++){
        off_t file_offset = i*BLOCK_SIZE;
        off_t page_align_offset = i*BLOCK_SIZE & ~(sysconf(_SC_PAGE_SIZE)-1);
        off_t in_page_offset = file_offset-page_align_offset;
        int mm_len = in_page_offset+BLOCK_SIZE;

        read(fd1, buf1, BLOCK_SIZE);
        buf2 = (char *)mmap(NULL, mm_len, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, page_align_offset);
        memcpy(buf2+in_page_offset, buf1, BLOCK_SIZE);
        munmap(buf2, mm_len);
    }
    clock_t t2 = clock();
    cout << "total size is: " << MAP_SIZE << endl;
    cout << "block size is: " << BLOCK_SIZE << endl;
    cout << "read+mmap use " << (clock() - t1) * 1.0 / CLOCKS_PER_SEC * 1000 << "ms" << endl;
}
void test4(int fd1, int fd2){
    // mmap+mmap
    char* buf1;
    char* buf2;
    clock_t t1 = clock();
    for(long i = 0; i < MAP_SIZE / BLOCK_SIZE; i++){
        off_t file_offset = i*BLOCK_SIZE;
        off_t page_align_offset = i*BLOCK_SIZE & ~(sysconf(_SC_PAGE_SIZE)-1);
        off_t in_page_offset = file_offset-page_align_offset;
        int mm_len = in_page_offset+BLOCK_SIZE;

        buf1 = (char *)mmap(NULL, mm_len, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, page_align_offset);
        buf2 = (char *)mmap(NULL, mm_len, PROT_READ|PROT_WRITE, MAP_SHARED, fd2, page_align_offset);
        memcpy(buf2+in_page_offset, buf1+in_page_offset, BLOCK_SIZE);

        munmap(buf1, mm_len);
        munmap(buf2, mm_len);
    }
    
    clock_t t2 = clock();
    cout << "total size is: " << MAP_SIZE << endl;
    cout << "block size is: " << BLOCK_SIZE << endl;
    cout << "mmap+mmap use " << (clock() - t1) * 1.0 / CLOCKS_PER_SEC * 1000 << "ms" << endl;
}


int main(int argc, char *argv[])
{
    int fd1, fd2;
    fd1 = shm_open("shm1", O_RDWR|O_CREAT, 0644);
    fd2 = shm_open("shm2", O_RDWR|O_CREAT, 0644);
    // fd1 = open("f1", O_RDWR|O_CREAT, 0644);
    // fd2 = open("f2", O_RDWR|O_CREAT, 0644);
    if(fd1 < 0 || fd2 < 0){
        printf("shm_open failed\n");
        exit(1);
    }
    
    if (ftruncate(fd1, MAP_SIZE) < 0){
        printf("ftruncate failed\n");
        exit(1);
    }
    if (ftruncate(fd2, MAP_SIZE) < 0){
        printf("ftruncate failed\n");
        exit(1);
    }

    test1(fd1, fd2);
    test2(fd1, fd2);
    test3(fd1, fd2);
    test4(fd1, fd2);
}

显示 mmap 较慢的结果之一:

total size is: 1073741824

block size is: 4096

read+write use 254.571ms

total size is: 1073741824

block size is: 4096

mmap+write use 486.342ms

total size is: 1073741824

block size is: 4096

read+mmap use 537.277ms

total size is: 1073741824

block size is: 4096

mmap+mmap use 737.734ms

【问题讨论】:

  • 我想看看 mmap 速度的优势。 你从哪里得到mmap() 更快的想法?它不是。所有read()write() 所要做的就是将数据从存储复制到进程地址空间,或者从进程地址空间复制到存储。 mmap() 必须这样做在进程地址空间中创建虚拟页面,分配物理页面, 创建虚拟到物理的映射。 那么就可以复制数据了。 (是的,您可以使用read()write() 获得页面错误,但您可以控制它。您使用mmap() 有多个页面错误...)
  • read()write() 需要将fd1 数据复制到用户空间,而不是将它们复制到fd2mmap 只需要构建页面映射并复制一次
  • 所以? mmap()SLOW。你刚刚学会了。
  • 我不这么认为。比较 1 和 4,1 做了两份,4 只做了一份和两次缺页。但是缺页只是修改页表。它应该比复制数据更快
  • 你不这么认为吗? Well, one Linus Torvalds would vehemently disagree with you 我强烈怀疑他比你更了解如何处理内存。同样:mmap()SLOW。请注意,您看到 mmap() 花费的时间是 read()/write() 的三倍。这完全与 Linus 响应的性能差异相同...加上 ça 变化,加上 c'est la meme 选择

标签: linux file copy mmap


【解决方案1】:

clock 函数测量进程使用的 CPU 时间。它对工作是在进程上下文还是内核上下文中完成很敏感。

当一个线程从文件中读取一些数据时,无论是通过调用read还是访问mmap,在读取操作完成之前,该线程都无法进一步向前推进。最有效的做法是立即执行与该操作相关的所有 CPU 工作,以便该进程可以恢复工作。由于您衡量的是在这种情况下完成了多少工作,因此您实际上惩罚一个更有效的操作,以便以最有效的方式完成更多工作。

所以这种类型的测量实际上奖励了低效率。进程上下文之外完成的工作不计算在内,因此该方法效率低下的工作越多(同时使进程等待更长的时间),您说它使用的时间就越少。

我认为主要问题是您并没有明确的问题。为什么测量“速度”意味着测量使用的进程 CPU 时间?为什么不测量挂墙时间?

【讨论】:

  • 很抱歉我对cpu时间和wall time不太了解,谢谢你告诉我这个。但是我用gettimeofday重新计算了时间,结果还是一样
【解决方案2】:

补充实验test567

#include <pthread.h>
#include <unistd.h>
#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>

constexpr long MAP_SIZE = 1L * 1024 * 1024;
constexpr int BLOCK_SIZE = 4;

using namespace std;

void test1(int fd1, int fd2)
{
    // read+write
    char buf[BLOCK_SIZE];
    lseek(fd1, 0, SEEK_SET);
    lseek(fd2, 0, SEEK_SET);

    timeval tv1;
    gettimeofday(&tv1, nullptr);
    // clock_t t1 = clock();
    for (long i = 0; i < MAP_SIZE / BLOCK_SIZE; i++)
    {
        // use for fair
        off_t file_offset = i * BLOCK_SIZE;
        off_t page_align_offset = i * BLOCK_SIZE & ~(sysconf(_SC_PAGE_SIZE) - 1);
        off_t in_page_offset = file_offset - page_align_offset;
        int mm_len = in_page_offset + BLOCK_SIZE;

        read(fd1, buf, BLOCK_SIZE);
        write(fd2, buf, BLOCK_SIZE);
    }
    // clock_t t2 = clock();
    // cout << "total size is: " << MAP_SIZE << endl;
    // cout << "block size is: " << BLOCK_SIZE << endl;
    // cout << "read+write use " 
    //      << (t2 - t1) * 1.0 / CLOCKS_PER_SEC * 1000 << "ms" << endl;
    timeval tv2;
    gettimeofday(&tv2, nullptr);
    cout << "total size is: " << MAP_SIZE << endl;
    cout << "block size is: " << BLOCK_SIZE << endl;
    cout << "read+write use " 
        << (tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000.0 
        << "ms" << endl;
}
void test2(int fd1, int fd2)
{
    // mmap+write
    char *buf;
    lseek(fd1, 0, SEEK_SET);
    lseek(fd2, 0, SEEK_SET);

    timeval tv1;
    gettimeofday(&tv1, nullptr);
    // clock_t t1 = clock();
    for (long i = 0; i < MAP_SIZE / BLOCK_SIZE; i++)
    {
        off_t file_offset = i * BLOCK_SIZE;
        off_t page_align_offset = i * BLOCK_SIZE & ~(sysconf(_SC_PAGE_SIZE) - 1);
        off_t in_page_offset = file_offset - page_align_offset;
        int mm_len = in_page_offset + BLOCK_SIZE;

        buf = (char *)mmap(NULL, mm_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd1, page_align_offset);
        write(fd2, buf + in_page_offset, BLOCK_SIZE);
        munmap(buf, mm_len);
    }
    // clock_t t2 = clock();
    // cout << "total size is: " << MAP_SIZE << endl;
    // cout << "block size is: " << BLOCK_SIZE << endl;
    // cout << "mmap+write use " 
    //      << (clock() - t1) * 1.0 / CLOCKS_PER_SEC * 1000 << "ms" << endl;

    timeval tv2;
    gettimeofday(&tv2, nullptr);
    cout << "total size is: " << MAP_SIZE << endl;
    cout << "block size is: " << BLOCK_SIZE << endl;
    cout << "mmap+write use " 
        << (tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000.0 
        << "ms" << endl;
}
void test3(int fd1, int fd2)
{
    // read+mmap
    char buf1[BLOCK_SIZE];
    char *buf2;
    lseek(fd1, 0, SEEK_SET);
    lseek(fd2, 0, SEEK_SET);

    timeval tv1;
    gettimeofday(&tv1, nullptr);
    // clock_t t1 = clock();
    for (long i = 0; i < MAP_SIZE / BLOCK_SIZE; i++)
    {
        off_t file_offset = i * BLOCK_SIZE;
        off_t page_align_offset = i * BLOCK_SIZE & ~(sysconf(_SC_PAGE_SIZE) - 1);
        off_t in_page_offset = file_offset - page_align_offset;
        int mm_len = in_page_offset + BLOCK_SIZE;

        read(fd1, buf1, BLOCK_SIZE);
        buf2 = (char *)mmap(NULL, mm_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd2, page_align_offset);
        memcpy(buf2 + in_page_offset, buf1, BLOCK_SIZE);
        munmap(buf2, mm_len);
    }
    // clock_t t2 = clock();
    // cout << "total size is: " << MAP_SIZE << endl;
    // cout << "block size is: " << BLOCK_SIZE << endl;
    // cout << "read+mmap use " << (clock() - t1) * 1.0 / CLOCKS_PER_SEC * 1000 
    //      << "ms" << endl;
    timeval tv2;
    gettimeofday(&tv2, nullptr);
    cout << "total size is: " << MAP_SIZE << endl;
    cout << "block size is: " << BLOCK_SIZE << endl;
    cout << "read+mmap use " 
        << (tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000.0 
        << "ms" << endl;
}
void test4(int fd1, int fd2)
{
    // mmap+mmap
    char *buf1;
    char *buf2;

    timeval tv1;
    gettimeofday(&tv1, nullptr);
    // clock_t t1 = clock();

    for (long i = 0; i < MAP_SIZE / BLOCK_SIZE; i++)
    {
        off_t file_offset = i * BLOCK_SIZE;
        off_t page_align_offset = i * BLOCK_SIZE & ~(sysconf(_SC_PAGE_SIZE) - 1);
        off_t in_page_offset = file_offset - page_align_offset;
        int mm_len = in_page_offset + BLOCK_SIZE;

        buf1 = (char *)mmap(NULL, mm_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd1, page_align_offset);
        buf2 = (char *)mmap(NULL, mm_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd2, page_align_offset);
        memcpy(buf2 + in_page_offset, buf1 + in_page_offset, BLOCK_SIZE);

        munmap(buf1, mm_len);
        munmap(buf2, mm_len);
    }

    // clock_t t2 = clock();
    // cout << "total size is: " << MAP_SIZE << endl;
    // cout << "block size is: " << BLOCK_SIZE << endl;
    // cout << "mmap+mmap use " 
    //      << (clock() - t1) * 1.0 / CLOCKS_PER_SEC * 1000 
    //      << "ms" << endl;
    timeval tv2;
    gettimeofday(&tv2, nullptr);
    cout << "total size is: " << MAP_SIZE << endl;
    cout << "block size is: " << BLOCK_SIZE << endl;
    cout << "mmap+mmap use " 
        << (tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000.0 
        << "ms" << endl;
}
void test5(int fd1, int fd2)
{
    // mmap(large)+write
    char *buf;
    lseek(fd1, 0, SEEK_SET);
    lseek(fd2, 0, SEEK_SET);

    timeval tv1;
    gettimeofday(&tv1, nullptr);
    // clock_t t1 = clock();

    // for(long i = 0; i < MAP_SIZE / BLOCK_SIZE; i++){
    //     off_t file_offset = i*BLOCK_SIZE;
    //     off_t page_align_offset = i*BLOCK_SIZE & ~(sysconf(_SC_PAGE_SIZE)-1);
    //     off_t in_page_offset = file_offset - page_align_offset;
    //     int mm_len = in_page_offset + BLOCK_SIZE;

    //     buf = (char *)mmap(NULL, mm_len, PROT_READ|PROT_WRITE, MAP_SHARED, fd1, page_align_offset);
    //     write(fd2, buf+in_page_offset, BLOCK_SIZE);
    //     munmap(buf, mm_len);
    // }
    buf = (char *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd1, 0);
    for (long i = 0; i < MAP_SIZE / BLOCK_SIZE; i++)
    {
        write(fd2, buf + i * BLOCK_SIZE, BLOCK_SIZE);
    }
    munmap(buf, MAP_SIZE);
    // clock_t t2 = clock();
    // cout << "total size is: " << MAP_SIZE << endl;
    // cout << "block size is: " << BLOCK_SIZE << endl;
    // cout << "mmap+write use " 
    //      << (clock() - t1) * 1.0 / CLOCKS_PER_SEC * 1000 
    //      << "ms" << endl;

    timeval tv2;
    gettimeofday(&tv2, nullptr);
    cout << "total size is: " << MAP_SIZE << endl;
    cout << "block size is: " << BLOCK_SIZE << endl;
    cout << "mmap(large)+write use " 
        << (tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000.0 
        << "ms" << endl;
}
void test6(int fd1, int fd2)
{
    // read+mmap(large)
    char buf1[BLOCK_SIZE];
    char *buf2;
    lseek(fd1, 0, SEEK_SET);
    lseek(fd2, 0, SEEK_SET);

    timeval tv1;
    gettimeofday(&tv1, nullptr);
    // clock_t t1 = clock();
    buf2 = (char *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd2, 0);
    for (long i = 0; i < MAP_SIZE / BLOCK_SIZE; i++)
    {
        read(fd1, buf2 + i * BLOCK_SIZE, BLOCK_SIZE);
    }
    munmap(buf2, MAP_SIZE);
    // clock_t t2 = clock();
    // cout << "total size is: " << MAP_SIZE << endl;
    // cout << "block size is: " << BLOCK_SIZE << endl;
    // cout << "read+mmap use " << (clock() - t1) * 1.0 / CLOCKS_PER_SEC * 1000 << "ms" << endl;
    timeval tv2;
    gettimeofday(&tv2, nullptr);
    cout << "total size is: " << MAP_SIZE << endl;
    cout << "block size is: " << BLOCK_SIZE << endl;
    cout << "read+mmap(large) use "
        << (tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000.0
        << "ms" << endl;
}
void test7(int fd1, int fd2)
{
    // mmap(large)+mmap(large)
    char *buf1;
    char *buf2;

    timeval tv1;
    gettimeofday(&tv1, nullptr);
    // clock_t t1 = clock();
    buf1 = (char *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd1, 0);
    buf2 = (char *)mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd2, 0);
    for (long i = 0; i < MAP_SIZE / BLOCK_SIZE; i++)
    {
        memcpy(buf2 + i * BLOCK_SIZE, buf1 + i * BLOCK_SIZE, BLOCK_SIZE);
    }
    munmap(buf1, MAP_SIZE);
    munmap(buf2, MAP_SIZE);
    // clock_t t2 = clock();
    // cout << "total size is: " << MAP_SIZE << endl;
    // cout << "block size is: " << BLOCK_SIZE << endl;
    // cout << "mmap+mmap use "
    //      << (clock() - t1) * 1.0 / CLOCKS_PER_SEC * 1000 << "ms" << endl;
    timeval tv2;
    gettimeofday(&tv2, nullptr);
    cout << "total size is: " << MAP_SIZE << endl;
    cout << "block size is: " << BLOCK_SIZE << endl;
    cout << "mmap(large)+mmap(large) use "
        << (tv2.tv_sec - tv1.tv_sec) * 1000 + (tv2.tv_usec - tv1.tv_usec) / 1000.0
        << "ms" << endl;
}

int main(int argc, char *argv[])
{
    int fd1, fd2;
    fd1 = shm_open("shm1", O_RDWR | O_CREAT, 0644);
    fd2 = shm_open("shm2", O_RDWR | O_CREAT, 0644);
    // fd1 = open("f1", O_RDWR|O_CREAT, 0644);
    // fd2 = open("f2", O_RDWR|O_CREAT, 0644);
    if (fd1 < 0 || fd2 < 0)
    {
        printf("shm_open failed\n");
        exit(1);
    }

    if (ftruncate(fd1, MAP_SIZE) < 0)
    {
        printf("ftruncate failed\n");
        exit(1);
    }
    if (ftruncate(fd2, MAP_SIZE) < 0)
    {
        printf("ftruncate failed\n");
        exit(1);
    }

    test1(fd1, fd2);
    test2(fd1, fd2);
    test3(fd1, fd2);
    test4(fd1, fd2);
    test5(fd1, fd2);
    test6(fd1, fd2);
    test7(fd1, fd2);
}

total size is: 1048576
block size is: 4
read+write use 113.286ms
total size is: 1048576
block size is: 4
mmap+write use 353.928ms
total size is: 1048576
block size is: 4
read+mmap use 355.605ms
total size is: 1048576
block size is: 4
mmap+mmap use 602.21ms
total size is: 1048576
block size is: 4
mmap(large)+write use 57.245ms
total size is: 1048576
block size is: 4
read+mmap(large) use 43.094ms
total size is: 1048576
block size is: 4
mmap(large)+mmap(large) use 0.611ms

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-24
    • 1970-01-01
    • 2018-07-01
    • 2015-02-02
    • 2010-09-07
    • 2015-08-14
    • 1970-01-01
    相关资源
    最近更新 更多