Skip to content

perf: Avoid per-event heap allocation in SensorSetter#9579

Open
tomekzaw wants to merge 1 commit into
mainfrom
@tomekzaw/sensorsetter-avoid-per-event-alloc
Open

perf: Avoid per-event heap allocation in SensorSetter#9579
tomekzaw wants to merge 1 commit into
mainfrom
@tomekzaw/sensorsetter-avoid-per-event-alloc

Conversation

@tomekzaw

@tomekzaw tomekzaw commented Jun 1, 2026

Copy link
Copy Markdown
Member

Summary

SensorSetter::sensorSetter is the Java→C++ callback invoked on every sensor sample (up to 60–200 Hz). It called value->getRegion(0, size), which heap-allocates (the two-argument getRegion overload returns a std::unique_ptr<jfloat[]>, i.e. a new jfloat[size]) on each invocation.

This switches to the allocation-free getRegion(start, length, buf) overload, reading directly into a fixed stack buffer — eliminating the per-event allocation:

static constexpr size_t kMaxSize = 7;
const size_t size = std::min(static_cast<size_t>(value->size()), kMaxSize);
jfloat elements[kMaxSize];
value->getRegion(0, size, elements);
double array[kMaxSize];
for (size_t i = 0; i < size; i++) array[i] = elements[i];
callback_(array, orientationDegrees);

Why this reduces allocation: the win is heap vs. stack. The two-argument getRegion(0, size) overload returns a std::unique_ptr<jfloat[]> — a heap new[]/delete[] (with allocator overhead) once per sensor event. The replacement jfloat elements[7] is an automatic stack array: no allocator involved, just a stack-pointer adjustment, essentially free. The extra jfloat[7] buffer exists only because getRegion yields jfloat while the callback wants double; adding a stack buffer is free, removing the heap allocation is the gain. Net: one heap allocation per event → zero.

As a side effect, the read is now clamped to the fixed array[7] destination, removing the previous unchecked assumption that the incoming Java array never exceeds 7 floats.

Behavior is otherwise unchanged.

Test plan

Run an example using useAnimatedSensor (accelerometer / gyroscope / rotation) on Android and confirm sensor values flow through as before.

`sensorSetter` is invoked on every sensor sample (up to 60–200 Hz) and
called `getRegion(0, size)`, which heap-allocates a `std::vector<jfloat>`
each time. Read into a fixed stack buffer with the allocation-free
`getRegion(start, length, buf)` overload instead.

Also clamp the read to the fixed `array[7]` destination, removing the
previous unchecked assumption that the Java array never exceeds 7 floats.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes the Android sensor callback path (SensorSetter::sensorSetter) by eliminating a per-sample heap allocation when reading FloatArray data from Java into C++, which is invoked at high frequency (60–200 Hz). It also adds a hard clamp to the destination buffer size (7), avoiding the previous unchecked assumption that the incoming array length never exceeds 7.

Changes:

  • Replaces getRegion(start, length) (allocating std::vector<jfloat>) with the allocation-free getRegion(start, length, buf) overload.
  • Clamps the copied element count to a fixed maximum of 7 to match the native destination buffer.
  • Adds <algorithm> include for std::min.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants