为什么写入不同大小的文件会导致不同的速度?

为什么写入不同大小的文件会导致不同的速度?

以下是该dd命令的一些输出:

$ dd if=/dev/zero of=test.file bs=10M count=1
1+0 records in
1+0 records out
10485760 bytes (10 MB) copied, 0.130214 s, 80.5 MB/s
$ dd if=/dev/zero of=test.file bs=1M count=1
1+0 records in
1+0 records out
1048576 bytes (1.0 MB) copied, 0.00683995 s, 153 MB/s
$ dd if=/dev/zero of=test.file bs=512k count=1
1+0 records in
1+0 records out
524288 bytes (524 kB) copied, 0.0029348 s, 179 MB/s
$ dd if=/dev/zero of=test.file bs=10k count=1
1+0 records in
1+0 records out
10240 bytes (10 kB) copied, 0.000199126 s, 51.4 MB/s
$ dd if=/dev/zero of=test.file bs=1k count=1
1+0 records in
1+0 records out
1024 bytes (1.0 kB) copied, 0.000133526 s, 7.7 MB/s
$ dd if=/dev/zero of=test.file bs=1 count=1
1+0 records in
1+0 records out
1 byte (1 B) copied, 0.000149828 s, 6.7 kB/s

为什么在 10k 时会出现速度“驼峰”?
为什么文件非常小的时候速度似乎慢得多?

答案1

观察到的效果基本上是正确的,但是该方法至少由于两个原因而存在缺陷。

  • 测试的是各种缓冲区大小,而不是各种文件大小。这是因为 dd 被指示从相同的两个文件读取和写入,而只有bs=(读取和写入块的大小)参数会发生变化。

  • /dev/zero 的输出速度将受到读取请求的大小(或缓冲区)的影响,因为它将提供所请求的字节数。这是由输入输出记录报告的。

1 字节时速度明显变慢,这是因为创建文件所花的时间比读取和写入内容所花的时间要多,而到了 10K 的速度高峰,文件创建不再是整个试验时间的主要部分。此后的速度下降由测试文件所在介质的突发写入(连续)速度决定。

要对各种文件大小的相对速度进行基准测试,您需要在文件之间分割一定量的数据,例如:

100MB to 10 files 
10MB  to 100 files, 
1MB   to 100 files,
500KB to 204 files, 
64K   to 1600 files
1K    to 102400 files. 

其他因素也会发挥作用,例如媒体的块/扇区大小以及文件系统(块大小)单个文件的最小分配大小。


对 sawdust 评论的回应。

dd 命令中的“bs”表示块大小,结合计数,指定合适的传输大小来计算 I/O 速率

这两个参数可以选出合适且精确的大小,但是如果 非常低,I/O 速率会更差bs=。据我所知(man dd,GNU Coreutils),dd 参数的bs=BYTES解释是“一次读取和写入最多 BYTES 个字节”。对我来说,这听起来像是:

int bs  // read upto bs bytes in each read operation.
byte[] buffer
while ( bs = file.readIntobufferAndReturnBytesRead(buffer) ) {
     ... use data in buffer
}

换句话说,许多小的读写操作将比一个大的读写操作花费更长的时间。如果您认为这是一个虚假的说法,欢迎您进行一个实验,首先用茶匙装满一个 5L 的水桶,然后比较使用杯子完成相同任务所需的时间。如果您恰好更熟悉 的内部工作原理dd,请提供您的证据,说明为什么bs=是“块大小”以及它在代码中的含义。

这就是为什么我的第二句话是有道理的:

修复 trial.sh:

MB10="
10MB   1
1MB    10
512KB  20
256KB  40
128KB  80
64KB   160
32KB   320
16KB   640
4KB    2560
1KB    10240
512    20480
64     163840
1      10485760
"

BLOCKS100="
10MB 100
1MB 100
512KB 100
256KB 100
128KB 100
64KB 100
32KB 100
16KB 100
4KB 100
1KB 100
512 100
256 100
128 100
64 100
32 100
16 100
4 100
1 100
"
function trial {
    BS=(`echo -e "$1" | awk '{print $1}'`)
    CO=(`echo -e "$1" | awk '{print $2}'`)

    printf "%-8s %-18s %7s %12s %8s\n" bs count data time speed

    for ((i=0;i<${#BS[@]};i++ )); do 
        printf "%-8s %-18s" "bs=${BS[i]}" "count=${CO[i]}"
        dd if=/dev/zero of=/dev/null bs=${BS[i]} count=${CO[i]} \
            |& awk '/bytes/ { printf "%10s  %-12s %8s\n", $3" "$4, $6, $8""$9 }'

    done
    echo
}

trial "$BLOCKS100"
trial "$MB10"

$ sh trial.sh
bs       count                 data         time    speed
bs=10MB  count=100           (1.0 GB)  0.781882      1.3GB/s
bs=1MB   count=100           (100 MB)  0.0625649     1.6GB/s
bs=512KB count=100            (51 MB)  0.0193581     2.6GB/s
bs=256KB count=100            (26 MB)  0.00990991    2.6GB/s
bs=128KB count=100            (13 MB)  0.00517942    2.5GB/s
bs=64KB  count=100           (6.4 MB)  0.00299067    2.1GB/s
bs=32KB  count=100           (3.2 MB)  0.00166215    1.9GB/s
bs=16KB  count=100           (1.6 MB)  0.00111013    1.4GB/s
bs=4KB   count=100           (400 kB)  0.000552862   724MB/s
bs=1KB   count=100           (100 kB)  0.000385104   260MB/s
bs=512   count=100            (51 kB)  0.000357936   143MB/s
bs=256   count=100            (26 kB)  0.000509282  50.3MB/s
bs=128   count=100            (13 kB)  0.000419117  30.5MB/s
bs=64    count=100           (6.4 kB)  0.00035179   18.2MB/s
bs=32    count=100           (3.2 kB)  0.000352209   9.1MB/s
bs=16    count=100           (1.6 kB)  0.000341594   4.7MB/s
bs=4     count=100            (400 B)  0.000336425   1.2MB/s
bs=1     count=100            (100 B)  0.000345085   290kB/s

bs       count                 data         time    speed     
bs=10MB  count=1              (10 MB)  0.0177581     563MB/s   566MB/s    567MB/s
bs=1MB   count=10             (10 MB)  0.00759677    1.3GB/s   1.3GB/s    1.2GB/s
bs=512KB count=20             (10 MB)  0.00545376    1.9GB/s   1.9GB/s    1.8GB/s
bs=256KB count=40             (10 MB)  0.00416945    2.5GB/s   2.4GB/s    2.4GB/s
bs=128KB count=80             (10 MB)  0.00396747    2.6GB/s   2.5GB/s    2.6GB/s
bs=64KB  count=160            (10 MB)  0.00446215    2.3GB/s   2.5GB/s    2.5GB/s
bs=32KB  count=320            (10 MB)  0.00451118    2.3GB/s   2.4GB/s    2.4GB/s
bs=16KB  count=640            (10 MB)  0.003922      2.6GB/s   2.5GB/s    2.5GB/s
bs=4KB   count=2560           (10 MB)  0.00613164    1.7GB/s   1.6GB/s    1.7GB/s
bs=1KB   count=10240          (10 MB)  0.0154327     664MB/s   655MB/s    626MB/s
bs=512   count=20480          (10 MB)  0.0279125     376MB/s   348MB/s    314MB/s
bs=64    count=163840         (10 MB)  0.212944     49.2MB/s  50.5MB/s   52.5MB/s
bs=1     count=10485760       (10 MB)  16.0154       655kB/s   652kB/s    640kB/s

第二个缺陷的相关部分是当数据大小恒定(10 MB)时。对于非常小的块,速度显然会更慢。我不确定如何解释 bs=10MB 时的“下降”,但我猜这是由于 dd 如何处理大块的缓冲。


我必须弄清楚这一点(感谢 sawdust 对我的假设的挑战)...

看起来我对缓冲区大小==bs 的假设是错误的,但也不完全错误,因为 bs 参数会影响大小,正如打印缓冲区大小的 dd 所示。这意味着我的第二个缺陷与页面大小为 4K 的系统上小于 8K 的文件无关:

$ ./dd  if=/dev/zero of=/dev/null  bs=1  count=1
OUTPUT_BLOCK_SLOP: 4095
MALLOC INPUT_BLOCK_SLOP: 8195, ibuf: 8196
1+0 records in
1+0 records out
1 byte (1 B) copied, 0.000572 s, 1.7 kB/s


$ ./dd  if=/dev/zero of=/dev/null  bs=1805  count=1
OUTPUT_BLOCK_SLOP: 4095
MALLOC INPUT_BLOCK_SLOP: 8195, ibuf: 10000
1+0 records in
1+0 records out
1805 bytes (1.8 kB) copied, 0.000450266 s, 4.0 MB/s

(来自 coreutils 8.20 的 dd.c)

 line:text
   21:   #define SWAB_ALIGN_OFFSET 2
   97:   #define INPUT_BLOCK_SLOP (2 * SWAB_ALIGN_OFFSET + 2 * page_size - 1)
   98:   #define OUTPUT_BLOCK_SLOP (page_size - 1)

 1872:  real_buf = malloc (input_blocksize + INPUT_BLOCK_SLOP);         // ibuf
 1889:      real_obuf = malloc (output_blocksize + OUTPUT_BLOCK_SLOP);  // obuf
                 // if conversion is on, othervise obuf=ibuf 

 2187:  page_size = getpagesize ();

人3 memcpy

   void *memcpy(void *dest, const void *src, size_t n);

   The memcpy() function copies n bytes from memory area src to memory area
   dest.  The memory areas must not overlap.  Use memmove(3) if the  memory
   areas do overlap.

答案2

小文件需要操作系统停下来为每个文件创建目录条目。因此 100mb 有 1 个目录条目,然后是数据。100 个 1mb 大小的文件有 100 个目录条目,每个条目都需要时间来创建。它还需要时间来定位下一个空闲扇区,并且需要更多时间来跳转到该扇区并开始写入。非常小的文件的计时会不准确。操作在内存中完成,并在操作系统需要时写入磁盘。计时表示在内存中执行该操作而不是在磁盘上执行该操作所花费的时间。除非您有 RAID、SSD 或其他超快速存储,否则任何 >100mb/s 都是内存计时。

相关内容