Skip to content

perf: parse computeMatrix matrices in Rust in read_matrix_file - #1492

Open
MisterSheikh wants to merge 1 commit into
deeptools:masterfrom
MisterSheikh:perf/matrix-reader
Open

MisterSheikh wants to merge 1 commit into
deeptools:masterfrom
MisterSheikh:perf/matrix-reader

Conversation

@MisterSheikh

Copy link
Copy Markdown

Closes #1490.

read_matrix_file parses the matrix line by line in Python, which takes about 20 s on an 81,918 x 3,600 matrix. This adds hp.r_read_matrix, which does the decompression and parsing in Rust in parallel. read_matrix_file returns exactly the same matrix, regions and groups as before. The old parser stays as a fallback for anything the fast path doesn't handle, such as file objects or ragged rows.

test_readMatrixFile.py runs both parsers on every matrix in the test data and checks that they match exactly. They also match on the public matrix from #1490, and plotHeatmap produces the same PNG.

Median of 2 runs on that matrix:

master this PR
read_matrix_file 20.3 s 2.6 s
plotHeatmap 33.0 s 15.2 s
plotProfile 22.4 s 4.4 s
computeMatrixOperations info 20.8 s 2.8 s

Peak memory is 8–20% lower.

Two notes:

heatmapper.read_matrix_file, used by plotHeatmap, plotProfile and
computeMatrixOperations, splits every line in Python and converts each
value with float(). For an 82k x 3,600 matrix this takes about 20 s, half
of plotHeatmap's runtime.

hp.r_read_matrix (matrixreader.rs) decompresses the file on one thread
and parses chunks of lines on a rayon pool with the GIL released. Values
are parsed with str::parse::<f64>, which is correctly rounded like
Python's float() and accepts nan/inf. It returns the parameter lines, the
six region fields per row and the values as a float64 bytearray.
read_matrix_file wraps the buffer with np.frombuffer as a MaskedArray
without a mask (the same type the per-line reader produced) and builds
regions and groups as before. The reader uses up to 16 threads from the
CPU affinity mask.

The per-line reader is kept as _read_matrix_file_legacy and is used for
anything the fast path does not handle: file objects, rows of unequal
length, tokens str::parse rejects (e.g. "1_0"), parameter lines after
data rows, or an hp module without r_read_matrix.

test_readMatrixFile.py reads every matrix in the test data with both
readers and checks that matrix type, dtype, shape, mask, value bits
(NaN and -0.0 included), regions, parameters, labels, boundaries and
sorting settings are identical, and that the fallback is taken for a file
object and for an unusual token.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf: computeMatrix and plotHeatmap/plotProfile spend most of their time on matrix I/O

1 participant