[WIP][PYTHON] Optimize PySpark broadcast serialization with Arrow - #57781
[WIP][PYTHON] Optimize PySpark broadcast serialization with Arrow#57781zhengruifeng wants to merge 8 commits into
Conversation
|
Benchmark results Environment: conda spark-dev-313, Python 3.13.12, PyArrow 24.0.0, ASV with 3 repeats. End-to-end cases use local[4], 4 input rows, and 4 partitions so broadcast work dominates. End-to-end broadcast of a native PyArrow table
The larger payload is about 44 MB. Arrow is approximately 3-5% faster there, although the difference is modest. Isolated serde for a native PyArrow table, 1M rows
Native PyArrow values already have efficient pickle support, so this is not the main target case. Isolated serde for the custom protocol example backed by list[float]
For 1M floats, combined serde falls from about 60.5 ms to 14.6 ms, approximately 4.2x faster. Retained worker payload memory falls from 32,289,080 bytes for the Python list and float objects to 8,000,000 bytes for the Arrow float64 buffer, approximately 75% less. |
What changes were proposed in this pull request?
This PR adds an optional useArrow argument to SparkContext.broadcast in PySpark Classic. Arrow IPC is selected only when all of the following are true:
Native PyArrow Table, RecordBatch, Array, and ChunkedArray values are supported directly.
A custom class must implement from_arrow as a class or static method and provide either to_arrow, arrow_c_stream, arrow_c_array, or arrow_array. The serialized payload contains small pickled class metadata followed by the Arrow IPC stream so the worker can invoke from_arrow.
If useArrow is None or False, PyArrow is unavailable, or the value does not implement the complete protocol, the existing pickle path is used. The public Broadcast.dump, Broadcast.load, and Broadcast.load_from_path methods remain pickle-specific.
Why are the changes needed?
Arrow-compatible Python objects already have a typed columnar representation. This opt-in protocol lets applications avoid generic object serialization and reconstruction for large Arrow-native state while retaining pickle as the compatibility path for arbitrary Python classes.
Explicit opt-in avoids changing serialization behavior for existing applications and lets custom classes control both conversion and reconstruction.
Does this PR introduce any user-facing change?
Yes. SparkContext.broadcast accepts useArrow: Optional[bool] = None. With useArrow=True, supported values use Arrow IPC and retain their native or custom reconstructed type on the driver and workers. The default behavior remains pickle.
How was this patch tested?
All Arrow broadcast tests are isolated in python/pyspark/tests/test_arrow_broadcast.py and registered in dev/sparktestsupport/modules.py under pyspark-core.
The encrypted variant could not complete locally because the environment crashes in the OpenSSL FIPS self-test. The existing pickle-only BroadcastTest.test_broadcast_with_encryption reproduces the identical crash, so this is not caused by the Arrow serialization path.
Was this patch authored or co-authored using generative AI tooling?
Generated-by: Codex (GPT-5)