fix(mnn): use getNumpyData() instead of getData() for output extraction (~3x rec speedup) - #717
Open
todorangrg wants to merge 1 commit into
Open
Conversation
MNNInferSession.__call__ extracted session output via np.array(out_tensor.getData()).reshape(out_shape), which marshals tensor contents through a slow per-element Python path. For a PP-OCRv6 medium recognizer output (~17MB), this cost ~454ms versus ~210ms for the actual GPU inference that produced it. Switch to getNumpyData(), which exposes the buffer directly. An explicit copy is required since getNumpyData() returns a view into memory owned by out_tensor; without it, out_tensor going out of scope or being reused across shape changes caused intermittent segfaults. On a 39-box real-world image, total recognition time dropped from ~22s to ~8.4s. Per-crop output extraction dropped from ~454ms to ~10ms. Fixes intermittent segfault risk from unmanaged zero-copy tensor lifetime as a side effect.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR optimizes MNN backend output extraction in MNNInferSession.__call__ by switching from per-element getData() to buffer-based getNumpyData(), reducing Python overhead during post-inference tensor conversion.
Changes:
- Replace
np.array(out_tensor.getData())withnp.array(out_tensor.getNumpyData(), copy=True)to accelerate output extraction. - Keep an explicit copy during conversion to avoid lifetime-related crashes when
out_tensorgoes out of scope or is reused.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
MNNInferSession.__call__inrapidocr/inference_engine/mnn/main.pyextractsthe session output via:
getData()marshals the tensor contents through a slow per-element Pythonpath rather than exposing the buffer directly. For a PP-OCRv6 medium
recognizer output of shape
(1, ~230, 18710)(~17MB), this step alone took~450ms on our test hardware (RK3588, OpenCL backend), compared to
~210ms for the actual GPU inference (
runSession) that produced it — i.e.result extraction was costing more than twice as much as inference itself.
On a real-world image with 39 detected text boxes, this pushed total
recognition time from an expected ~9s to ~22s.
Fix
Use the buffer-protocol-based
getNumpyData()instead, which is alreadyexposed on the MNN Python binding:
The explicit
copy=Trueis required —getNumpyData()returns a view intomemory owned by
out_tensor. Without an explicit copy, we observedintermittent segfaults once
out_tensorwent out of scope / was reusedacross calls with different session shapes. With the copy forced, this
was fully stable across repeated runs and varying input shapes/batches.
Results (RK3588, OpenCL backend, PP-OCRv6 medium recognizer)
__call__)The remaining ~10ms for extraction is a legitimate memcpy+reshape cost for
~17MB of data and is not further addressed here.
Testing
getData()path vianp.allcloseon sampled outputs.over repeated runs, including shape changes between calls).
Notes
This affects every user of the MNN engine backend, independent of model,
backend device (CPU/OpenCL/Vulkan), or hardware — it's a fixed cost in the
Python-level extraction path, not specific to our setup.