viirya commented on code in PR #57099:
URL: https://github.com/apache/spark/pull/57099#discussion_r3547004147
##########
python/pyspark/sql/conversion.py:
##########
@@ -980,6 +980,63 @@ class ArrowTableToRowsConversion:
Conversion from Arrow Table to Rows.
"""
+ @staticmethod
+ def _to_pylist(column: Union["pa.Array", "pa.ChunkedArray"]) -> List[Any]:
+ """
+ Equivalent to ``column.to_pylist()``, but converts (nested) list
columns in bulk
+ instead of one scalar at a time.
+
+ ``Array.to_pylist()`` materializes one Scalar per element; for list
types each row
+ additionally allocates a C++ scalar, a Python Scalar wrapper and a
Python Array
+ wrapper for the row's values before converting elements one by one,
which is
+ several times slower than converting the flattened child values in a
single pass
+ and slicing the resulting Python list per row (see
apache/arrow#50326). The values
+ themselves are still converted by Arrow's own ``to_pylist``, so
results are exactly
+ identical: ``None`` stays ``None`` and values inside numeric lists
stay Python ints,
+ unlike a pandas round trip which would coerce them to floats/NaN.
NumPy is used
+ only for the offsets (non-null integers) and the validity bitmap
(booleans), so no
+ value coercion can occur.
+
+ This can be removed once the minimum supported PyArrow version
includes the fix
+ for apache/arrow#50326.
+ """
+ import pyarrow as pa
+ import pyarrow.types as pa_types
+
+ try:
+ import numpy # noqa: F401
+ except ImportError:
+ return column.to_pylist()
Review Comment:
Good point — moved it to a module-level cached helper
(`_is_numpy_available()`) in 1fd52c8e351, so the try/import runs at most once
per process instead of per (recursive) invocation.
##########
python/benchmarks/bench_arrow.py:
##########
@@ -114,3 +114,43 @@ def time_long_with_nulls_to_pandas_ext(self, n_rows,
method):
def peakmem_long_with_nulls_to_pandas_ext(self, n_rows, method):
self.run_long_with_nulls_to_pandas_ext(n_rows, method)
+
+
+class ArrowListColumnToRowsBenchmark:
+ """
+ Benchmark for converting Arrow list-typed columns to Python rows, the hot
+ path of Arrow-optimized Python UDF inputs and Spark Connect collect().
+
+ ``baseline`` measures plain ``column.to_pylist()``; ``bulk`` measures
+ ``ArrowTableToRowsConversion._to_pylist`` (see apache/arrow#50326).
+ """
+
+ params = [
+ [100000, 1000000],
+ ["baseline", "bulk"],
+ ]
+ param_names = ["n_rows", "method"]
+
+ def setup(self, n_rows, method):
+ from pyspark.sql.conversion import ArrowTableToRowsConversion
+
+ self.list_of_strings = pa.array(
+ [[f"s{i}", f"t{i}"] for i in range(n_rows)],
type=pa.list_(pa.string())
+ )
+ self.nested_ints_with_nulls = pa.array(
+ [[[i, i + 1], None, [i + 2]] if i % 10 != 0 else None for i in
range(n_rows)],
+ type=pa.list_(pa.list_(pa.int32())),
+ )
+ if method == "bulk":
+ self.convert = ArrowTableToRowsConversion._to_pylist
+ else:
+ self.convert = lambda column: column.to_pylist()
+
+ def time_list_of_strings_to_rows(self, n_rows, method):
+ self.convert(self.list_of_strings)
+
+ def time_nested_ints_with_nulls_to_rows(self, n_rows, method):
+ self.convert(self.nested_ints_with_nulls)
+
+ def peakmem_list_of_strings_to_rows(self, n_rows, method):
Review Comment:
Added `peakmem` variants for the nested case plus a new
`array<struct<i:int,s:string>>` case (time + peakmem) in 1fd52c8e351. Measured
(1M rows): nested `list<list<int32>>` 862M → 862M (no change); `array<struct>`
885M → 921M (~4%). The flattened child conversion only adds a transient pointer
list — the leaf objects themselves are shared between the flat list and the
per-row slices — so the peak increase stays small.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]