Repository navigation
Improve Spill Performance: mmap the spill files #15321
Copy link
Copy link
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Is your feature request related to a problem or challenge?
Today when DataFusion spills files to disk, it uses the Arrow IPC format
Here is the code:
datafusion/datafusion/physical-plan/src/spill.rs
Lines 60 to 88 in 988a535
| pub(crate) fn spill_record_batches( | |
| batches: &[RecordBatch], | |
| path: PathBuf, | |
| schema: SchemaRef, | |
| ) -> Result<(usize, usize)> { | |
| let mut writer = IPCStreamWriter::new(path.as_ref(), schema.as_ref())?; | |
| for batch in batches { | |
| writer.write(batch)?; | |
| } | |
| writer.finish()?; | |
| debug!( | |
| "Spilled {} batches of total {} rows to disk, memory released {}", | |
| writer.num_batches, | |
| writer.num_rows, | |
| human_readable_size(writer.num_bytes), | |
| ); | |
| Ok((writer.num_rows, writer.num_bytes)) | |
| } | |
| fn read_spill(sender: Sender<Result<RecordBatch>>, path: &Path) -> Result<()> { | |
| let file = BufReader::new(File::open(path)?); | |
| let reader = StreamReader::try_new(file, None)?; | |
| for batch in reader { | |
| sender | |
| .blocking_send(batch.map_err(Into::into)) | |
| .map_err(|e| exec_datafusion_err!("{e}"))?; | |
| } | |
| Ok(()) | |
| } |
The IPC reader currently reads the spill files using file IO and into memory.
it is possible to use mmap to zero copy the contents of the files into memory. Here is an example of how to do so:
https://github.com/apache/arrow-rs/blob/main/arrow/examples/zero_copy_ipc.rs
- My testing on Add
with_skip_validationflag to IPCStreamReader,FileReaderandFileDecoderarrow-rs#7120 suggested mmap is 3x faster than file IO
Describe the solution you'd like
I would like to see if using mmap to read the spill files back in is faster
Describe alternatives you've considered
- Use mmap to read spill files
- Add / use a benchmark showing the peformance benefit of doing this
Additional context
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request
Activity
zebsme commentedon Mar 22, 2025
take
alamb commentedon Mar 22, 2025
BTW I am not sure the code is really in a great position to do this one yet -- it might help to wait for @2010YOUY01 (or help him) to pull some of the spilling code into its own structure -- see #15355
zebsme commentedon Mar 26, 2025
hi @alamb , Thanks for sharing your great example here. Following your approach, I noticed that when using an Arrow buffer with mmap as a field in the Decoder, running the tests produces the following output:
It seems that the drop method defined in memmap2 isn't being executed:
However, after switching to using mmap directly as a field, the related tests passed successfully.
2010YOUY01 commentedon Mar 26, 2025
This error is triggered by DataFusion's internal memory tracking component, instead of thrown by the OS. So likely there is something wrong with the spill logic, could you share the draft code?
zebsme commentedon Mar 26, 2025
In this issue, I've been experimenting with replacing StreamWriter/StreamReader with FileWriter/FileReader to evaluate potential performance improvements using mmap.
However directly replacement seems to cause some other problems, see #14868.
Those errors above are encountered with my own FileWriter and FileReader implementation. So I don't think it's an issue for our current spill implementation :)
zebsme commentedon Apr 8, 2025
hi @2010YOUY01 , create a draft here, zebsme#3.
This draft can reproduce the errors, and apply the code changes in comment will resolve all the test failures.
I'm uncertain if the munmap is responsible for memory allocation failures - could you please help review this ?
2010YOUY01 commentedon Apr 8, 2025
I took a look, but the reason for those test failures isn’t obvious to me. However, I think the implementation requires two changes:
SpillManager's read utility to read spill, instead of using a lower-level utility function in SMJ (I forgot to update the spill read path in refactor: Use SpillManager for all spilling scenarios #15405 🤦♂,will update it soonit's hard to refactor SMJ code, maybe we can ignore this and use the existing hack)IPCStreamWriterinstead ofIPCFileWriter, otherwise it will cause a regression for not supporting spilling dictionary type, see Use arrow IPC Stream format for spill files #14868. (unless we can also support spilling dictionaries inIPCFileWriter)zebsme commentedon Apr 8, 2025
Thanks for your reply @2010YOUY01 , and the benchmark shows that StreamReader with mmap has no performance improvement compared to current implementation:
So there is no need to enable mmap now, as we need stream format here.
2010YOUY01 commentedon Apr 8, 2025
Maybe it's already using
mmap🤔 I'll double-check it when I start to look into the performance, thank you for the experiments.zebsme commentedon Apr 8, 2025
StreamReader with mmap enabled: