我想计算两个大流(设备/文件)中的差异(不同字节)的数量。例如两个硬盘,或一个硬盘和/dev/zero
.
所涉及的程序必须很快,与输入的流一样快(例如 1GB/s,尽管 0.2GB/s 可能也可以),并且它们最多可以使用几 GB 的 RAM 和 tmp 文件。特别是,没有足够大的可用文件系统来存储要计算的差异。这些流的大小有几 TB。
计数不需要(事实上也不能)将空格或换行符与其他字符区别对待。这些流只是二进制的,不能解释为文本。
我说的是流,尽管这些设备实际上是可查找的。但出于速度原因,如果可能的话,所有操作最好通过一次流传输完成,无需查找。
到目前为止我尝试过的:
cmp -l /dev/sda /dev/sdb | wc
但这太慢了;wc
单独使用一个CPU核心> 50%,输出只有大约2MB / s,这太慢了100倍。
答案1
使用可查找文件,您可以并行化此操作。例如,您可以执行以下操作(未经测试):
# Number of jobs to run at once
jobs=4
# Block size
bs=512
# Total number of block to work with
size=1000000
# Or, to calculate the size automatically for one of the disks,
#size=$(( $(df -B 1 /dev/sda | tail -n1 | awk '{ print $2 }') / $bs ))
# Number of blocks per job
count=$(( $size / $jobs ))
for i in $(seq 1 $jobs) ; do
seek=$(( ($i - 1) * $count ))
cmp -l <(dd bs=$bs skip=$seek count=$count if=/dev/sdb1) <(dd bs=$bs skip=$seek count=$count if=/dev/sdb2) | wc &
done
dd
if 第一次启动时只会查找一次。
答案2
除非您的硬件支持,否则您不会获得 1GB/s。最快的 7200 rpm 硬盘在其部分表面上可以达到约 200MB/s,在整个表面上更接近 100MB 到 150MB。因此,您的“可能没问题”数字实际上高于理想数字,除非您的驱动器速度异常快( 10krpm,RAID0)。
cmp -l | wc
可能受到CPU的限制,因为你要求wc
计算字数,这有点复杂。cmp -l | wc -l
会减少CPU的负载。
除非您有非常快的磁盘,否则cmp -l | wc -l
可能已经受到 IO 限制。如果您使用整个磁盘带宽,并行化将无济于事。
如果cmp -l | wc -l
仍然受 CPU 限制,或者如果您想释放 CPU 来做其他事情,那么执行计数的临时程序会表现得更好。警告,未经测试。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUFFER_SIZE 1048576
unsigned char buf1[BUFFER_SIZE];
unsigned char buf2[BUFFER_SIZE];
int main (int argc, char *argv[]) {
FILE *f1, *f2;
unsigned long long total = 0, diffs = 0;
unsigned n1, n2, n, i;
f1 = fopen(argv[1], "r");
if (f1 == NULL) {perror(argv[1]); return 2;}
f2 = fopen(argv[2], "r");
if (f2 == NULL) {perror(argv[2]); return 2;}
do {
n1 = fread(buf1, 1, BUFFER_SIZE, f1);
n2 = fread(buf2, 1, BUFFER_SIZE, f2);
if (n1 > n2) n = n2; else n = n1;
for (i = 0; i < n; i++) if (buf1[i] != buf2[i]) ++diffs;
total += n;
} while (n1 == BUFFER_SIZE && n2 == BUFFER_SIZE);
printf("%llu\n", diffs);
if (ferror(f1)) {perror(argv[1]);}
if (ferror(f2)) {perror(argv[2]);}
if (n1 != n2) {
fprintf(stderr, "Stopped after %llu bytes.\n", total);
return 1;
}
return 0;
}