Skip to content

refactor: remove coremltools dependency and use torch.utils.cpp_extension to load kmeans1d#31

Draft
guru-desh wants to merge 9 commits into
apple:mainfrom
guru-desh:u/gurudesh/remove-coremltools-dependency-torch-cpp
Draft

refactor: remove coremltools dependency and use torch.utils.cpp_extension to load kmeans1d#31
guru-desh wants to merge 9 commits into
apple:mainfrom
guru-desh:u/gurudesh/remove-coremltools-dependency-torch-cpp

Conversation

@guru-desh

@guru-desh guru-desh commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Description

This PR removes coremltools as a required dependency for coreai-opt.

The only part of coremltools that was being used was kmeans1d. We have copied kmeans1d in coreai-opt using torch.utils.cpp_extension to compile the C++ code upon invocation.

Using torch.utils.cpp_extension allows us to have a more faithful refactor for kmeans1d. From here, we can then choose to explore alternatives like numba or optimizations for the kmeans1d C++ code. This PR is scoped to have the minimal amount of changes needed to remove coremltools as a dependency.

Thanks @pkmandke for the discussion and suggestions!

Testing

  1. We have equivalence tests to ensure that this copy of kmeans1d is equivalent to coremltools kmeans1d (tolerance is 1e-9)
  2. kmeans1d tests are copied over from coremltools
  3. We also have a hash check to check that the C++ file copied over is the same as the one that exists on coremltools. This means that we have a pure refactor of kmeans1d

@guru-desh guru-desh force-pushed the u/gurudesh/remove-coremltools-dependency-torch-cpp branch from cca4b22 to 9910570 Compare July 9, 2026 21:03
_DLL = None


def _dll():

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This file is mainly the same as coremltools._deps.kmeans1d except for this function that uses torch.utils.cpp_extension to compile _core.cpp

# be found in the LICENSE file or at https://opensource.org/licenses/BSD-3-Clause


# MIT License

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

TODO: Is this licensing correct?

@guru-desh guru-desh force-pushed the u/gurudesh/remove-coremltools-dependency-torch-cpp branch 3 times, most recently from 8c4d108 to a17f5c0 Compare July 10, 2026 00:05
# NOT inherit sysconfig flags and defaults to -O0, which made this
# tight-inner-loop, template-heavy DP code 10-15x slower per call than
# coremltools' prebuilt extension. Pass the same flags explicitly to match.
_EXTRA_CFLAGS = ["-std=c++11", "-O2", "-DNDEBUG"] + (

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Future PRs can look into using -O3 compilation flag and benchmark performance, but since this PR is meant as a pure refactor, we stick with -O2

Vendor coremltools' kmeans1d C++ core (_core.cpp, byte-for-byte from apple/coremltools; upstream kmeans1d 0.3.1, MIT) and JIT-compile it at runtime with torch.utils.cpp_extension, invoking its extern "C" entry points via ctypes. Provides cluster()/Clustered as a drop-in replacement for coremltools._deps._kmeans1d, with fail-loud input validation.
Point _efficient_kmeans and kmeans_fake_palettize at coreai_opt._utils._kmeans1d instead of coremltools._deps._kmeans1d. The cluster()/Clustered call contract is unchanged.
Drop the optional third-party kmeans1d import and _HAS_KMEANS1D flag in palettize_utils; import coreai_opt._utils._kmeans1d unconditionally. The sklearn fallback for vector k-means is unchanged.
Compare cluster() output against the coremltools oracle (unweighted, weighted, large-n, collapse, fp16 ties), assert fixed known-good cases and input validation, and add a canary that hard-fails if the vendored _core.cpp drifts from apple/coremltools main.
coremltools is no longer needed at runtime; move it (and its numpy<2.4 cap) into a coreml optional-dependency + self-referencing dependency-group, kept in default-groups so the test oracle is available. Ship the vendored _core.cpp and LICENSE as package data.
Palettization spawns many worker processes that each call
_kmeans1d.cluster(). Since the vendored extension JIT-compiles on first
use per-process, every worker independently raced the same on-disk
build lock: one worker compiled while the rest spin-waited for it
instead of just loading the finished .so, serializing a one-time
multi-minute compile across the critical path.

Add _kmeans1d.warmup() and call it once in the parent process before
each worker pool is created (KMeansPalettizer and the coreai-torch
palettize_utils compress pool), so every worker's load() call finds
the extension already built.
The vendored kmeans1d C++ extension was 10-15x slower per call than
coremltools' precompiled version (measured directly: ~0.9s vs ~0.06s
on a realistic-size weighted cluster() call), even with the actual
JIT-compile lock/cache fully warmed. This was the real cause of the
1.2x-2.6x palettization wall-clock regression seen in an end-to-end
LLM eval, not JIT-compile lock contention across parallel workers
(a prior "warmup()" fix targeting that, since reverted, made no
measurable difference).

Root cause: torch.utils.cpp_extension.load() does not inherit
CPython's sysconfig OPT/CFLAGS the way distutils/setuptools does, so
it defaults to -O0. coremltools' setup.py sets no explicit -O flag
either, but distutils prepends sysconfig's "-O2 -DNDEBUG" (the
standard python.org/conda default) ahead of its extra_compile_args,
so its shipped .so is effectively an -O2 build. This tight-inner-loop,
template-heavy DP algorithm is extremely sensitive to that gap.

Pass the same flags explicitly so torch's JIT build matches
coremltools' effective compile line. Numeric output is unaffected
(verified against the existing coremltools-oracle equivalence tests).
@guru-desh guru-desh force-pushed the u/gurudesh/remove-coremltools-dependency-torch-cpp branch from e858d3a to a64cf0c Compare July 10, 2026 09:08
@guru-desh guru-desh added the enhancement New feature or request label Jul 10, 2026
_DLL = None


def _dll():

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Were you able to identify if/why this re-compiles each time during parallel kmeans?

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants