If the destination supports zero, try to zero entire extent in one
request. This speeds up copying of large sparse images. Same logic is
used by nbdcopy.
Here is an example benchmark, copying empty 1 TiB qcow2 image:
$ qemu-img create -f qcow2 src.qcow2 1t
$ qemu-img create -f qcow2 dst.qcow2 1t
$ qemu-nbd --persistent --socket=/tmp/src.sock --format=qcow2 --read-only src.qcow2
$ qemu-nbd --persistent --socket=/tmp/dst.sock --format=qcow2 dst.qcow2
$ export SRC=nbd+unix:///?socket=/tmp/src.sock
$ export DST=nbd+unix:///?socket=/tmp/dst.sock
$ hyperfine -w3 \
"./copy-libev $SRC $DST" \
"qemu-img convert -n -W $SRC $DST" \
"../copy/nbdcopy --request-size=1048576 --requests=16 --connections=1 $SRC
$DST"
Benchmark #1: ./copy-libev nbd+unix:///?socket=/tmp/src.sock
nbd+unix:///?socket=/tmp/dst.sock
Time (mean ± σ): 940.9 ms ± 36.3 ms [User: 80.8 ms, System: 120.0 ms]
Range (min … max): 892.8 ms … 1005.3 ms 10 runs
Benchmark #2: qemu-img convert -n -W nbd+unix:///?socket=/tmp/src.sock
nbd+unix:///?socket=/tmp/dst.sock
Time (mean ± σ): 2.848 s ± 0.087 s [User: 241.7 ms, System: 253.9 ms]
Range (min … max): 2.740 s … 3.035 s 10 runs
Benchmark #3: ../copy/nbdcopy --request-size=1048576 --requests=16 --connections=1
nbd+unix:///?socket=/tmp/src.sock nbd+unix:///?socket=/tmp/dst.sock
Time (mean ± σ): 1.082 s ± 0.041 s [User: 77.6 ms, System: 100.9 ms]
Range (min … max): 1.043 s … 1.148 s 10 runs
Summary
'./copy-libev nbd+unix:///?socket=/tmp/src.sock
nbd+unix:///?socket=/tmp/dst.sock' ran
1.15 ± 0.06 times faster than '../copy/nbdcopy --request-size=1048576
--requests=16 --connections=1 nbd+unix:///?socket=/tmp/src.sock
nbd+unix:///?socket=/tmp/dst.sock'
3.03 ± 0.15 times faster than 'qemu-img convert -n -W
nbd+unix:///?socket=/tmp/src.sock nbd+unix:///?socket=/tmp/dst.sock'
Signed-off-by: Nir Soffer <nsoffer(a)redhat.com>
---
examples/copy-libev.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/examples/copy-libev.c b/examples/copy-libev.c
index 197d959..d9c8560 100644
--- a/examples/copy-libev.c
+++ b/examples/copy-libev.c
@@ -248,7 +248,7 @@ start_extents (struct request *r)
static void
next_extent (struct request *r)
{
- uint32_t limit = MIN (REQUEST_SIZE, size - offset);
+ uint32_t limit;
uint32_t length = 0;
bool is_zero;
@@ -256,6 +256,12 @@ next_extent (struct request *r)
is_zero = extents[extents_pos + 1] & LIBNBD_STATE_ZERO;
+ /* Zero can be much faster, so try to zero entire extent. */
+ if (is_zero && dst.can_zero)
+ limit = MIN (EXTENTS_SIZE, size - offset);
+ else
+ limit = MIN (REQUEST_SIZE, size - offset);
+
while (length < limit) {
DEBUG ("e%d: offset=%ld len=%ld zero=%d",
extents_pos / 2, offset, extents[extents_pos], is_zero);
--
2.26.3