mirror of
https://github.com/apache/arrow.git
synced 2026-10-29 17:15:39 +08:00
### Rationale for this change The Python memory/IO docs list `pyarrow.HdfsFile` as a `NativeFile` subclass, but that class no longer exists (legacy HDFS was deprecated in 2.0 and later removed). The `:class:` reference has no target, and HDFS access now uses `pyarrow.fs.HadoopFileSystem`, which returns regular `NativeFile` objects. It's the only unresolved pyarrow cross-reference on the page. ### What changes are included in this PR? Removes the stale `pyarrow.HdfsFile` bullet in docs/source/python/memory.rst. One line, docs only. ### Are these changes tested? Confirmed `pyarrow.HdfsFile` isn't in pyarrow, the ref appears only once, and all remaining `:class:` refs on the page resolve after removal. ### Are there any user-facing changes? Documentation only. Authored-by: Ritika shrestha <87307821+ritsth@users.noreply.github.com> Signed-off-by: AlenkaF <frim.alenka@gmail.com>
312 lines
9.5 KiB
ReStructuredText
312 lines
9.5 KiB
ReStructuredText
.. Licensed to the Apache Software Foundation (ASF) under one
|
|
.. or more contributor license agreements. See the NOTICE file
|
|
.. distributed with this work for additional information
|
|
.. regarding copyright ownership. The ASF licenses this file
|
|
.. to you under the Apache License, Version 2.0 (the
|
|
.. "License"); you may not use this file except in compliance
|
|
.. with the License. You may obtain a copy of the License at
|
|
|
|
.. http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
.. Unless required by applicable law or agreed to in writing,
|
|
.. software distributed under the License is distributed on an
|
|
.. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
.. KIND, either express or implied. See the License for the
|
|
.. specific language governing permissions and limitations
|
|
.. under the License.
|
|
|
|
.. currentmodule:: pyarrow
|
|
.. highlight:: python
|
|
|
|
.. _io:
|
|
|
|
========================
|
|
Memory and IO Interfaces
|
|
========================
|
|
|
|
This section will introduce you to the major concepts in PyArrow's memory
|
|
management and IO systems:
|
|
|
|
* Buffers
|
|
* Memory pools
|
|
* File-like and stream-like objects
|
|
|
|
Referencing and Allocating Memory
|
|
=================================
|
|
|
|
pyarrow.Buffer
|
|
--------------
|
|
|
|
The :class:`Buffer` object wraps the C++ :cpp:class:`arrow::Buffer` type
|
|
which is the primary tool for memory management in Apache Arrow in C++. It permits
|
|
higher-level array classes to safely interact with memory which they may or may
|
|
not own. ``arrow::Buffer`` can be zero-copy sliced to permit Buffers to cheaply
|
|
reference other Buffers, while preserving memory lifetime and clean
|
|
parent-child relationships.
|
|
|
|
There are many implementations of ``arrow::Buffer``, but they all provide a
|
|
standard interface: a data pointer and length. This is similar to Python's
|
|
built-in ``buffer protocol`` and ``memoryview`` objects.
|
|
|
|
A :class:`Buffer` can be created from any Python object implementing
|
|
the buffer protocol by calling the :func:`py_buffer` function. Let's consider
|
|
a bytes object:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> import pyarrow as pa
|
|
>>> data = b'abcdefghijklmnopqrstuvwxyz'
|
|
>>> buf = pa.py_buffer(data)
|
|
>>> buf
|
|
<pyarrow.Buffer address=... size=26 is_cpu=True is_mutable=False>
|
|
>>> buf.size
|
|
26
|
|
|
|
Creating a Buffer in this way does not allocate any memory; it is a zero-copy
|
|
view on the memory exported from the ``data`` bytes object.
|
|
|
|
External memory, under the form of a raw pointer and size, can also be
|
|
referenced using the :func:`foreign_buffer` function.
|
|
|
|
Buffers can be used in circumstances where a Python buffer or memoryview is
|
|
required, and such conversions are zero-copy:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> memoryview(buf)
|
|
<memory at ...>
|
|
|
|
The Buffer's :meth:`~Buffer.to_pybytes` method converts the Buffer's data to a
|
|
Python bytestring (thus making a copy of the data):
|
|
|
|
.. code-block:: python
|
|
|
|
>>> buf.to_pybytes()
|
|
b'abcdefghijklmnopqrstuvwxyz'
|
|
|
|
Memory Pools
|
|
------------
|
|
|
|
All memory allocations and deallocations (like ``malloc`` and ``free`` in C)
|
|
are tracked in an instance of :class:`MemoryPool`. This means that we can
|
|
then precisely track amount of memory that has been allocated:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> pa.total_allocated_bytes()
|
|
0
|
|
|
|
Let's allocate a resizable :class:`Buffer` from the default pool:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> buf = pa.allocate_buffer(1024, resizable=True)
|
|
>>> pa.total_allocated_bytes()
|
|
1024
|
|
>>> buf.resize(2048)
|
|
>>> pa.total_allocated_bytes()
|
|
2048
|
|
|
|
The default allocator requests memory in a minimum increment of 64 bytes. If
|
|
the buffer is garbage-collected, all of the memory is freed:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> buf = None
|
|
>>> pa.total_allocated_bytes()
|
|
0
|
|
|
|
Besides the default built-in memory pool, there may be additional memory pools
|
|
to choose from (such as `jemalloc <http://jemalloc.net/>`_)
|
|
depending on how Arrow was built. One can get the backend name for a memory
|
|
pool::
|
|
|
|
>>> pa.default_memory_pool().backend_name
|
|
'mimalloc'
|
|
|
|
.. seealso::
|
|
:ref:`API documentation for memory pools <api.memory_pool>`.
|
|
|
|
.. seealso::
|
|
On-GPU buffers using Arrow's optional :doc:`CUDA integration <integration/cuda>`.
|
|
|
|
|
|
Input and Output
|
|
================
|
|
|
|
.. _io.native_file:
|
|
|
|
The Arrow C++ libraries have several abstract interfaces for different kinds of
|
|
IO objects:
|
|
|
|
* Read-only streams
|
|
* Read-only files supporting random access
|
|
* Write-only streams
|
|
* Write-only files supporting random access
|
|
* File supporting reads, writes, and random access
|
|
|
|
In the interest of making these objects behave more like Python's built-in
|
|
``file`` objects, we have defined a :class:`~pyarrow.NativeFile` base class
|
|
which implements the same API as regular Python file objects.
|
|
|
|
:class:`~pyarrow.NativeFile` has some important features which make it
|
|
preferable to using Python files with PyArrow where possible:
|
|
|
|
* Other Arrow classes can access the internal C++ IO objects natively, and do
|
|
not need to acquire the Python GIL
|
|
* Native C++ IO may be able to do zero-copy IO, such as with memory maps
|
|
|
|
There are several kinds of :class:`~pyarrow.NativeFile` options available:
|
|
|
|
* :class:`~pyarrow.OSFile`, a native file that uses your operating system's
|
|
file descriptors
|
|
* :class:`~pyarrow.MemoryMappedFile`, for reading (zero-copy) and writing with
|
|
memory maps
|
|
* :class:`~pyarrow.BufferReader`, for reading :class:`~pyarrow.Buffer` objects
|
|
as a file
|
|
* :class:`~pyarrow.BufferOutputStream`, for writing data in-memory, producing a
|
|
Buffer at the end
|
|
* :class:`~pyarrow.FixedSizeBufferWriter`, for writing data into an already
|
|
allocated Buffer
|
|
* :class:`~pyarrow.PythonFile`, for interfacing with Python file objects in C++
|
|
* :class:`~pyarrow.CompressedInputStream` and
|
|
:class:`~pyarrow.CompressedOutputStream`, for on-the-fly compression or
|
|
decompression to/from another stream
|
|
|
|
There are also high-level APIs to make instantiating common kinds of streams
|
|
easier.
|
|
|
|
High-Level API
|
|
--------------
|
|
|
|
Input Streams
|
|
~~~~~~~~~~~~~
|
|
|
|
The :func:`~pyarrow.input_stream` function allows creating a readable
|
|
:class:`~pyarrow.NativeFile` from various kinds of sources.
|
|
|
|
* If passed a :class:`~pyarrow.Buffer` or a ``memoryview`` object, a
|
|
:class:`~pyarrow.BufferReader` will be returned:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> buf = memoryview(b"some data")
|
|
>>> stream = pa.input_stream(buf)
|
|
>>> stream.read(4)
|
|
b'some'
|
|
|
|
* If passed a string or file path, it will open the given file on disk
|
|
for reading, creating a :class:`~pyarrow.OSFile`. Optionally, the file
|
|
can be compressed: if its filename ends with a recognized extension
|
|
such as ``.gz``, its contents will automatically be decompressed on
|
|
reading.
|
|
|
|
.. code-block:: python
|
|
|
|
>>> import gzip
|
|
>>> with gzip.open('example.gz', 'wb') as f:
|
|
... f.write(b'some data\n' * 3)
|
|
30
|
|
>>> stream = pa.input_stream('example.gz')
|
|
>>> stream.read()
|
|
b'some data\nsome data\nsome data\n'
|
|
|
|
* If passed a Python file object, it will wrapped in a :class:`PythonFile`
|
|
such that the Arrow C++ libraries can read data from it (at the expense
|
|
of a slight overhead).
|
|
|
|
Output Streams
|
|
~~~~~~~~~~~~~~
|
|
|
|
:func:`~pyarrow.output_stream` is the equivalent function for output streams
|
|
and allows creating a writable :class:`~pyarrow.NativeFile`. It has the same
|
|
features as explained above for :func:`~pyarrow.input_stream`, such as being
|
|
able to write to buffers or do on-the-fly compression.
|
|
|
|
.. code-block:: python
|
|
|
|
>>> with pa.output_stream('example1.dat') as stream:
|
|
... stream.write(b'some data')
|
|
9
|
|
>>> with open('example1.dat', 'rb') as f:
|
|
... f.read()
|
|
b'some data'
|
|
|
|
|
|
On-Disk and Memory Mapped Files
|
|
-------------------------------
|
|
|
|
PyArrow includes two ways to interact with data on disk: standard operating
|
|
system-level file APIs, and memory-mapped files. In regular Python we can
|
|
write:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> with open('example2.dat', 'wb') as f:
|
|
... f.write(b'some example data')
|
|
17
|
|
|
|
Using pyarrow's :class:`~pyarrow.OSFile` class, you can write:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> with pa.OSFile('example3.dat', 'wb') as f:
|
|
... f.write(b'some example data')
|
|
17
|
|
|
|
For reading files, you can use :class:`~pyarrow.OSFile` or
|
|
:class:`~pyarrow.MemoryMappedFile`. The difference between these is that
|
|
:class:`~pyarrow.OSFile` allocates new memory on each read, like Python file
|
|
objects. In reads from memory maps, the library constructs a buffer referencing
|
|
the mapped memory without any memory allocation or copying:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> file_obj = pa.OSFile('example2.dat')
|
|
>>> mmap = pa.memory_map('example3.dat')
|
|
>>> file_obj.read(4)
|
|
b'some'
|
|
>>> mmap.read(4)
|
|
b'some'
|
|
|
|
The ``read`` method implements the standard Python file ``read`` API. To read
|
|
into Arrow Buffer objects, use ``read_buffer``:
|
|
|
|
.. code-block:: python
|
|
|
|
>>> mmap.seek(0)
|
|
0
|
|
>>> buf = mmap.read_buffer(4)
|
|
>>> buf
|
|
<pyarrow.Buffer address=... size=4 is_cpu=True is_mutable=False>
|
|
>>> buf.to_pybytes()
|
|
b'some'
|
|
|
|
Many tools in PyArrow, particular the Apache Parquet interface and the file and
|
|
stream messaging tools, are more efficient when used with these ``NativeFile``
|
|
types than with normal Python file objects.
|
|
|
|
In-Memory Reading and Writing
|
|
-----------------------------
|
|
|
|
To assist with serialization and deserialization of in-memory data, we have
|
|
file interfaces that can read and write to Arrow Buffers.
|
|
|
|
.. code-block:: python
|
|
|
|
>>> writer = pa.BufferOutputStream()
|
|
>>> writer.write(b'hello, friends')
|
|
14
|
|
>>> buf = writer.getvalue()
|
|
>>> buf
|
|
<pyarrow.Buffer address=... size=14 is_cpu=True is_mutable=True>
|
|
>>> buf.size
|
|
14
|
|
>>> reader = pa.BufferReader(buf)
|
|
>>> reader.seek(7)
|
|
7
|
|
>>> reader.read(7)
|
|
b'friends'
|
|
|
|
These have similar semantics to Python's built-in ``io.BytesIO``.
|