Skip to content

Avoid temporary numeric boxes when pushing into specialized dynamic arrays #294

Closed

Description

@dimensionscape

Appending a numeric value to a specialized Array constructs a temporary boxed Dynamic, passes it through the virtual __push interface, then immediately converts it back to the backing array's numeric element type. Indexed appends use the existing Variant setter path and avoid that box/unbox round trip.

This is common buffer and collection building, rather than a rare input pattern. The boxed values also create avoidable GC work.

Tested at d734565 on Windows x64, Haxe 4.3.7, MSVC 19.44.35213, release HXCPP_M64.

The probe builds an Array using either:

for (i in 1...n) array.push(i + 1000);

or:

for (i in 1...n) array[i] = i + 1000;

Both start with the same first numeric element, have the same final length and contents, and use the same specialized backing type. Float cases use i + 1000.5. Values are outside the small constant-box cache. Capacity is either reserved before timing or allowed to grow normally. Array construction, reserve and full value verification are outside timing. One warmup, median of seven samples, full GC before each sample, one million final elements.

Elements Capacity reserved push Indexed append
Int yes 6.960 ms 2.216 ms
Float yes 5.891 ms 2.662 ms
Int no 8.502 ms 4.663 ms
Float no 11.128 ms 6.766 ms

With capacity reserved, push was about 3.1 times slower for ints and 2.2 times slower for floats. These are focused append timings, not an application-wide speedup or measurements of an implemented fix.

Generated C++ calls array->push(value) for the first path and array->set(index,value) for the control. In include/cpp/VirtualArray.h, push and Add call EnsureStorage(inVal) and then base->__push(Dynamic(inVal)). The numeric Array_obj::_push override converts that Dynamic to ELEM. The indexed setter instead calls base->set(index,inVal), whose cpp::Variant argument stores numeric payloads directly without allocating a numeric wrapper.

Avoid the temporary numeric box for specialized dynamic-array appends, using the existing typed/Variant machinery where appropriate. Preserve push's returned length, widening, null behavior, fixed backing arrays, exact Int64 values, special floats and GC barriers. The same push/Add pattern should be considered together. This can be addressed entirely within libhxcpp without compiler or stdlib changes.

I added conventional local coverage first in test/regression/DynamicArrayPush, with Main.hx, build.hxml and stdout.txt. It checks integer and Float contents, push return values, mixed pushes, exact Int64 preservation during widening, a shared fixed backing array, NaN/infinities and negative zero. All checks pass on the current runtime; the test protects behavior during an optimization and contains no timing assertions.

No runtime implementation changes have been made.

Activity

  1. added a commit that references this issue on Oct 27, 2026
    a59383c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions