[Libguestfs] [libnbd PATCH] python: Allow control over copy/share of nbd.Buffer
Richard W.M. Jones
rjones at redhat.com
Tue Jun 14 14:39:40 UTC 2022
On Tue, Jun 14, 2022 at 07:58:08AM -0500, Eric Blake wrote:
> Add new methods nbd.Buffer.{to,from}_buffer that avoid the copying
> present in the existing nbd.Buffer.{to,from}_bytearray. The reduction
> in copies possible by this approach is no longer quite as necessary,
> now that aio_p{read,write} have been converted to take buffers
> directly. However, there is still one (marginal) benefit: if you use
> h.set_pread_initialize(False), and create a new buffer for every I/O
> (rather than reusing a buffer pool), nbd.Buffer(n) handed to
> h.aio_pread then buf.to_buffer() is slightly faster than handing a
> bytearray(n) directly to h.aio_pread, because we are able to skip the
> step of pre-zeroing the buffer.
> ---
>
> Instead of adding a copy=True/False parameter to the existing API as I
> had previously suggested, I decided that a new API made more sense.
>
> generator/Python.ml | 39 ++++++++++++++++++++++----
> python/t/585-aio-buffer-share.py | 47 ++++++++++++++++++++++++++++++++
> 2 files changed, 80 insertions(+), 6 deletions(-)
> create mode 100644 python/t/585-aio-buffer-share.py
>
> diff --git a/generator/Python.ml b/generator/Python.ml
> index 03a7e6b..cb89ccd 100644
> --- a/generator/Python.ml
> +++ b/generator/Python.ml
> @@ -735,6 +735,21 @@ let
> '''Allocate an uninitialized AIO buffer used for nbd.aio_pread.'''
> self._o = libnbdmod.alloc_aio_buffer(len)
>
> + @classmethod
> + def from_buffer(cls, buf):
> + '''Create an AIO buffer that shares an existing buffer-like object.
> +
> + Because the buffer is shared, changes to the original are visible
> + to nbd.aio_pwrite, and changes in nbd.aio_pread are visible to the
> + original.
> + '''
> + self = cls(0)
> + # Ensure that buf is already buffer-like
> + with memoryview(buf):
> + self._o = buf
> + self._init = True
> + return self
> +
> @classmethod
> def from_bytearray(cls, ba):
> '''Create an AIO buffer from a bytearray or other buffer-like object.
> @@ -743,16 +758,28 @@ let
> bytearray constructor. Otherwise, ba is copied. Either way, the
> resulting AIO buffer is independent from the original.
> '''
> - self = cls(0)
> - self._o = bytearray(ba)
> - self._init = True
> - return self
> + return cls.from_buffer(bytearray(ba))
>
> - def to_bytearray(self):
> - '''Copy an AIO buffer into a bytearray.'''
> + def to_buffer(self):
> + '''Return a shared view of the AIO buffer contents.
> +
> + This exposes the underlying buffer; changes to the buffer are
> + visible to nbd.aio_pwrite, and changes from nbd.aio_pread are
> + visible in the buffer.
> + '''
> if not hasattr(self, '_init'):
> self._o = bytearray(len(self._o))
> self._init = True
> + return self._o
> +
> + def to_bytearray(self):
> + '''Copy an AIO buffer into a bytearray.
> +
> + This copies the contents of an AIO buffer to a new bytearray, which
> + remains independent from the original.
> + '''
> + if not hasattr(self, '_init'):
> + return bytearray(len(self._o))
> return bytearray(self._o)
>
> def size(self):
> diff --git a/python/t/585-aio-buffer-share.py b/python/t/585-aio-buffer-share.py
> new file mode 100644
> index 0000000..21752b7
> --- /dev/null
> +++ b/python/t/585-aio-buffer-share.py
> @@ -0,0 +1,47 @@
> +# libnbd Python bindings
> +# Copyright (C) 2010-2022 Red Hat Inc.
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 2 of the License, or
> +# (at your option) any later version.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +#
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> +
> +# This tests the nbd.Buffer is_zero method. We can do this entirely
> +# without using the NBD protocol.
> +
> +import nbd
> +
> +# Use of to/from_bytearray always creates copies
> +ba = bytearray(512)
> +buf = nbd.Buffer.from_bytearray(ba)
> +ba.append(1)
> +assert len(ba) == 513
> +assert len(buf) == 512
> +assert buf.is_zero()
> +assert buf.to_bytearray() is not ba
> +
> +# Use of to/from_buffer shares the same buffer
> +buf = nbd.Buffer.from_buffer(ba)
> +assert not buf.is_zero()
> +assert len(buf) == 513
> +ba.pop()
> +assert buf.is_zero()
> +assert len(buf) == 512
> +assert buf.to_buffer() is ba
> +
> +# Even though nbd.Buffer(n) start uninitialized, we sanitize before exporting.
> +# This test cheats and examines the private member ._init
> +buf = nbd.Buffer(512)
> +assert buf.is_zero()
> +assert not hasattr(buf, '_init')
> +assert buf.to_buffer() == bytearray(512)
> +assert hasattr(buf, '_init')
> --
> 2.36.1
Acked-by: Richard W.M. Jones <rjones at redhat.com>
Rich.
--
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-p2v converts physical machines to virtual machines. Boot with a
live CD or over the network (PXE) and turn machines into KVM guests.
http://libguestfs.org/virt-v2v
More information about the Libguestfs
mailing list