Skip to content

perf(c): profile and optimize hot-path kernels #74

Open

Description

@davidbudzynski

Motivation

Profiling the current package shows that nearly all representative runtime is spent in the compiled C entry points, not R dispatch. This issue tracks performance work separately from the shared-memory safety fixes in #55.

Candidate work

  • Remove the redundant lv initialization in psort before the value is unconditionally assigned.
  • Replace full-size hash tables in funique and psort with growable tables sized to the observed distinct count, especially for low-cardinality data.
  • Fuse default initialization and matching passes in vswitch.
  • Add a bounded fallback or heap strategy to topn for adversarial ordered inputs while preserving the small-n fast path.
  • Evaluate fused initialization and mixed-type conversion in psum without changing NA/NaN or integer-overflow semantics.
  • Evaluate direct serialization in shareData; zero-copy/protocol redesign remains separate in shareData() without deep copy? #43.

Acceptance

  • Record before/after benchmarks for representative large-vector workloads.
  • Preserve ordering, attributes, factors, encoding behavior, fromLast, and NA/NaN semantics.
  • Keep public API and shared-memory protocol changes out of this issue unless separately agreed.
  • Use separate, focused pull requests for each optimization family.

Activity

  1. added 2 commits that reference this issue on Sep 28, 2026
  2. davidbudzynski commented on Sep 28, 2026

    @davidbudzynski
    CollaboratorAuthor

    Status update on the candidate list, re-verified against the current master.

    Done

    Dropping — no longer applicable

    • Remove the redundant lv initialization in psort. There is no initializer to remove. src/psort.c:474 reads int *restrict lv = (int*)R_alloc(xlen, sizeof(int)); and every element is assigned before use at src/psort.c:478. The adjacent pos array is genuinely memset-initialised because it is a counting array, so that one stays. No code change needed.

    Still open

    • Growable hash tables in funique and psort. Confirmed still full-size: src/dup.c:84, src/dupLen.c:78, src/dupLen.c:121, src/dupLen.c:324, and src/psort.c:227 / src/psort.c:264 all size their table M up front via R_Calloc(M, int). Best remaining candidate for low-cardinality data.
    • Fuse default initialization and matching passes in vswitch. Still two passes: the init loop at src/vswitch.c:137-139 writes the default (pans[j]= nonna ? pna[j & namask] : NA_LOGICAL;) before the separate match loop.
    • Bounded fallback for topn on adversarial ordered input. Unchanged; the len0 > 2000 / len0 > 1500 radix shortcut at src/topn.c:44 is still the only large-n path.
    • Direct serialization in shareData. Unchanged, still serialize() + rawConnectionValue() in R/call.R:79-90.

    Leaving this issue open for the remaining four. The zero-copy protocol redesign stays in #43 per the original split.

    Note that the long-vector counters noted in #61 still intersect this work in src/dup.c:813-817,861 and src/psort.c:389-408,569-616; worth sequencing the hash-table change after that audit so the sizing arithmetic is done on R_xlen_t from the start.

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