Implementation is not quite as simple as adding the flag, because of how Arrow lays out memory in IPC. Hence, the patch makes a few changes, beyond setting the flag:
- Each record batch, after reading, is "split" by coping each array in the batch into a separate allocation.
- After creating the Arrow Table, the references to the record batches are dropped.
split_blocks
is set so that Arrow has more opportunities to find zero-copy conversions.use_threads
is unset so that Arrow converts one column at a time, to minimize memory usage.
Two graphs are attached, using the same demo.py, except once with self_destruct
set to True
and once set to False
.
When it's not enabled, you can see a sharp spike in memory usage corresponding to the to_pandas
call, before the Python process is OOMKilled.
With the flag enabled, you still see a sharp spike, but it flatlines when it reaches the limit of available memory, then continues normally.
The peak still occurs because jemalloc (the malloc implementation used by Arrow) does still cache some allocations, and it may not necessarily reuse them.
Once we are near exhausting available memory, jemalloc appears to reuse those allocations to avoid getting OOMKilled.
This is something I admit I still don't fully understand and would like to investigate.