簡體   English   中英

如何有效地在文件中寫入大量的NULL字節序列?

[英]How to efficiently write a large sequence of NULL bytes in a file?

, an offset and a length, and I need to write NULL bytes from in the file described by (note: it never occurs at the end of the file). 我有一個文件描述符 ,一個偏移量和一個長度,我需要從描述的文件中的寫入 NULL字節(注意:它永遠不會出現在文件的末尾)。

除了使用填充了NULL的緩沖區並在循環中重復寫入之外,是否有一種有效的方法可以做到這一點? NULL的序列可以達到16Mo,我目前使用大小為512的緩沖區(=〜 write(2)約30k)。

你可以嘗試mmap荷蘭國際集團在所需的恰好所需要的尺寸偏差和映射,然后簡單地調用文件memset

編輯:基於@jthill發布的代碼,這是一個簡單的例子,演示如何進行比較..

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

void create(int fsize)
{
  FILE *fd = fopen("data", "wb");
  fseek(fd, fsize - 1, SEEK_SET);
  fputc(0, fd);
  fclose(fd);
}

void seek_write(const char* data, int wsize, int seek, int dsize)
{
  int fd = open("data", O_RDWR);
  // Now seek_write
  if (lseek(fd, seek, SEEK_SET) != seek)
    perror("seek?"), abort();
  // Now write in requested blocks..
  for (int c = dsize / wsize; c--;)
    if (write(fd, data, wsize) != wsize)
      perror("write?"), abort();
  close(fd);
}

void mmap_memset(int wsize, int seek, int dsize)
{
  int fd = open("data", O_RDWR);
  void* map = mmap(0, dsize + seek, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  if (map == MAP_FAILED)
    perror("mmap?"), abort();
  memset((char*)map + seek, 0, dsize);
  munmap(map, dsize);
  close(fd);
}

int main(int c, char **v)
{
  struct timeval start, end;
  long long ts1, ts2;
  int wsize = c>1 ? atoi(*++v) : 512;
  int seek  = c>2 ? atoi(*++v) : 0;
  int reps  = c>3 ? atoi(*++v) : 1000;
  int dsize = c>4 ? atoi(*++v) : 16*1024*1024;
  int fsize = c>5 ? atoi(*++v) : 32*1024*1024;

  // Create the file and grow...
  create(fsize);

  char *data = mmap(0, wsize, PROT_READ, MAP_ANON | MAP_PRIVATE, 0, 0);

  printf("Starting write...\n");
  gettimeofday(&start, NULL);
  for (int i = 0;i < reps; ++i)
    seek_write(data, wsize, seek, dsize);
  gettimeofday(&end, NULL);

  ts1 = ((end.tv_sec - start.tv_sec) * 1000000) + (end.tv_usec - start.tv_usec);

  printf("Starting mmap...\n");
  gettimeofday(&start, NULL);
  for (int i = 0;i < reps; ++i)
    mmap_memset(wsize, seek, dsize);
  gettimeofday(&end, NULL);

  ts2 = ((end.tv_sec - start.tv_sec) * 1000000) + (end.tv_usec - start.tv_usec);

  printf("write: %lld us, %f us\nmmap: %lld us, %f us", ts1, (double)ts1/reps, ts2, (double)ts2/reps);
}

注意:如果提供的偏移沒有對齊(通常在頁面邊界上), mmap不喜歡它,所以,如果你可以在長度+偏移量中映射並且只是從偏移量設置它可能更好(或者,如果你能保證一個很好的對齊偏移,這也會工作..)

如您所見,兩個操作之間的差異是lseekmap + seek )然后是writememset )。 我認為這是一個公平的比較(如果有人想解決任何問題,請隨意。)

我也使用MAP_SHARED而不是MAP_PRIVATE ,兩者之間存在顯着差異,后者執行寫時復制,這可能要慢得多!

在我不那么強大的系統上,我得到:

> ./fwrite 4096 1234
> Starting write...
> Starting mmap...
> write: 14767898 us, 14767.898000 us
> mmap: 6619623 us, 6619.623000 us

我認為這表明mmap + memset更快?

如果您正在運行Linux並且文件系統支持稀疏文件,您可以嘗試使用帶有FALLOC_FL_PUNCH_HOLE標志的fallocate(2)在文件中fallocate(2) 雖然我沒有測試它,但我希望它會很快。

在Linux上,您可以使用splice(2)/dev/zero復制數據。

這非常有效,因為大多數工作都是在內核中完成的。

其他操作系統可以提供類似的功能(即sendfile )。

更新

我忘記了可以在文件中間打孔的fallocate(2)

從下面看,16M的I / O做得很差,只有一次,大於20ms。 這本身就是可感知的。

小貼士:

  • I / O值得關注,因為
  • 做很多事情導致了可悲的延誤。
  • 512字節的寫入傷害了很多。
  • 4096字節寫入沒有。

在內存或磁盤上的偏移並不重要,所以calloc什么應該做得很好(你可以試試)。

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

int main(int c, char **v)
{
    int wsiz = c>1 ? atoi(*++v) : 512;
    int seek = c>2 ? atoi(*++v) : 0;
    int woff = c>3 ? atoi(*++v) : 0;
    int fsiz = c>4 ? atoi(*++v) : 16 * 1024 * 1024;
    int reps = c>5 ? atoi(*++v) : 1000;

    printf("writesize %d, seek  %d, align %d, filesize %d, reps %d\n",
           wsiz, seek, woff, fsiz, reps);

    if (wsiz<=0|seek<0|woff<0|fsiz<wsiz)
        return 1;

    int fd = open("data", O_CREAT | O_RDWR, 0700);
    char *data = mmap(0, 2*wsiz, PROT_READ, MAP_ANON | MAP_PRIVATE, 0, 0);

    for (int t = reps; t--; lseek(fd, seek, 0))
        for (int c = fsiz / wsiz; c--;)
            if (write(fd, data+woff, wsiz) != wsiz)
                perror("write?"), abort();

    return close(fd);
}
cc  -o bin/wipetest -g -O   --std=c11 -march=native -pipe -Wall -Wno-parentheses    wipetest.c
------------------------------------------------------
writesize 512, seek  0, align 0, filesize 16777216, reps 1000

real    0m20.727s
user    0m0.513s
sys 0m20.220s
------------------------------------------------------
writesize 4096, seek  0, align 0, filesize 16777216, reps 1000

real    0m3.889s
user    0m0.077s
sys 0m3.687s
------------------------------------------------------
writesize 16777216, seek  0, align 0, filesize 16777216, reps 1000

real    0m3.205s
user    0m0.000s
sys 0m3.203s
------------------------------------------------------
writesize 512, seek  500, align 0, filesize 16777216, reps 1000

real    0m23.829s
user    0m0.463s
sys 0m23.247s
------------------------------------------------------
writesize 4096, seek  500, align 0, filesize 16777216, reps 1000

real    0m5.531s
user    0m0.053s
sys 0m5.480s
------------------------------------------------------
writesize 16777216, seek  500, align 0, filesize 16777216, reps 1000

real    0m3.435s
user    0m0.000s
sys 0m3.433s
------------------------------------------------------
writesize 512, seek  0, align 12, filesize 16777216, reps 1000

real    0m21.478s
user    0m0.537s
sys 0m20.820s
------------------------------------------------------
writesize 4096, seek  0, align 12, filesize 16777216, reps 1000

real    0m3.722s
user    0m0.057s
sys 0m3.667s
------------------------------------------------------
writesize 16777216, seek  0, align 12, filesize 16777216, reps 1000

real    0m3.232s
user    0m0.000s
sys 0m3.233s
------------------------------------------------------
writesize 512, seek  500, align 12, filesize 16777216, reps 1000

real    0m23.775s
user    0m0.550s
sys 0m23.113s
------------------------------------------------------
writesize 4096, seek  500, align 12, filesize 16777216, reps 1000

real    0m5.566s
user    0m0.050s
sys 0m5.517s
------------------------------------------------------
writesize 16777216, seek  500, align 12, filesize 16777216, reps 1000

real    0m3.277s
user    0m0.000s
sys 0m3.277s

lseek()到位置並write()一個大緩沖區一次(或多次緩沖一次)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM