Repository navigation
Fix shell-out helpers and pass read-only parameters by const reference - #6
Merged
Merged
Conversation
fnv1aHash() piped through `python3.10 ../hash_script.py`, a script that is not in the repository, so debugArray() always threw. Compute the 64-bit FNV-1a hash of the array's row-major bytes directly instead. The VLM example built a shell command by concatenating ModelPath. Spawn decode.py with posix_spawnp and an argv array so the path is passed verbatim and no shell is involved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pass mx::array, std::string and std::shared_ptr parameters that are only read by const reference instead of by value, as reported by clang-tidy performance-unnecessary-value-param. Declarations and definitions are updated together. Virtual functions (Embedding::forward, Linear::forward and Prompt::prepare) are left unchanged so their overrides keep matching. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟢 Approval recommended
The helper replacements are focused, the signature changes are aligned, and no blocking correctness issues were identified.
No open issues 🎉
What changed in this PR
This PR removes shell dependencies from two helpers and reduces unnecessary parameter copies across the MLX layers and model code.
Changes:
- Replaces Python-based array hashing with an in-process 64-bit FNV-1a implementation.
- Uses
posix_spawnpto launch the VLM detokenizer without shell interpretation. - Changes read-only parameters to const references, keeping declarations and definitions aligned and leaving virtual interfaces unchanged.
| File | Description |
|---|---|
| model/whisper/decoding.h | Updates decoding declarations to const references. |
| model/whisper/decoding.cpp | Matches decoding definitions to updated declarations. |
| model/whisper_transcribe.h | Updates model parameters in transcription declarations. |
| model/whisper_transcribe.cpp | Matches transcription definitions to updated declarations. |
| model/vlm_base.h | Updates the attention-mask input declaration. |
| model/vlm_base.cpp | Passes the attention-mask input by const reference. |
| model/mllama/language.h | Updates attention and decoder cache declarations. |
| model/mllama/language.cpp | Passes cache pointers by const reference. |
| model/llm/transformer.h | Updates transformer array parameter declarations. |
| model/llm/transformer.cpp | Uses const references for arrays and helper strings. |
| model/converter.h | Updates weight-path parameter declarations. |
| model/converter.cpp | Passes weight paths by const reference. |
| mlx/quantized.h | Updates quantization factory declarations. |
| mlx/quantized.cpp | Passes source module pointers by const reference. |
| mlx/positional_encoding.h | Updates the RoPE input declaration. |
| mlx/positional_encoding.cpp | Passes RoPE input by const reference. |
| mlx/normalization.h | Updates normalization input declarations. |
| mlx/normalization.cpp | Passes normalization inputs by const reference. |
| mlx/embedding.h | Updates the non-virtual asLinear declaration. |
| mlx/embedding.cpp | Passes asLinear input by const reference. |
| mlx/convolution.h | Updates convolution input declarations. |
| mlx/convolution.cpp | Passes convolution inputs by const reference. |
| mlx/base.h | Updates module registration and parameter interfaces. |
| mlx/base.cpp | Implements native hashing and matching parameter changes. |
| mlx/activations.h | Updates activation input declarations. |
| mlx/activations.cpp | Passes activation inputs by const reference. |
| example/vlm.cpp | Launches the detokenizer with explicit process arguments. |
Review effort: Balanced
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.
Summary
Code-quality fixes from a static-analysis pass (GitRoll / clang-tidy). There are no API changes for callers.
1. Stop shelling out (
fix:commit)fnv1aHash()(mlx/base.cpp) saved the array to./temp_array.npyand ranpopen("python3.10 ../hash_script.py ...").hash_script.pydoesn't exist in the repo, sodebugArray()always threw. It now computes a 64-bit FNV-1a hash over the array's row-major bytes in C++, usingmx::contiguous+eval(). There's no temp file, no Python, and no shell.example/vlm.cppusedstd::system("python3.10 ../example/decode.py " + ModelPath + " output.npy"). It now runs the same command throughposix_spawnpwith an argv array.ModelPathis passed verbatim, so paths with spaces or shell characters work and nothing is interpreted by a shell. The interpreter (python3.10), script and output file are unchanged. If the spawn fails, it logs an error, as before.2.
const &for read-only parameters (refactor:commit)About 45
mx::array/std::string/std::shared_ptrparameters that are only read now takeconst &. Header declarations and definitions are changed together.I deliberately left these alone:
Embedding::forward,Linear::forward,Prompt::prepare. Their overrides (QuantizedEmbedding,QuantizedLinear, the*Promptclasses) would also have to change, so they're out of scope here.std::moverather thanconst &, so they belong in a separate change.Not changed
bugprone-branch-clone: an earlier scan flagged 4 "identical if/else branches", inllm/transformer.cpp:104,llm/transformer.h:177,mllama/language.cpp:335andvlm_base.cpp:351. All 4 are false positives. They came from a run that was missing the spdlog/fmt include paths. The branches really do differ:nn::RMSNormvs the Gemmallm::RMSNorm,RopeScalingvs{}, and Cross vs Self decoder layers. With the realcompile_commands.json, clang-tidy reports none of them.whisper.cppinteger division (Channels / 2 - 1,NState / NHead): this matches the reference Python's//, so it's intended.whisper_transcribe.cpp:46still builds the ffmpeg command string and callspopen, soFilePathisn't quoted. Fixing that needs a spawn plus a pipe, which is a bigger change. I'd suggest a follow-up.Verification
All of this ran on Linux x86_64 with clang 23 and MLX 0.32.4 (CPU wheel):
-Wall -Wextra -Wpedantic -Werror). The same 29 project translation units compile in both. The ones that fail are the same before and after, and none are caused by this PR:gemma3.cpp, which hits a libstdc++ 15std::optionalconstraint error. Its error output is identical before and after.performance-unnecessary-value-param: 93 → 14 (the virtual and std::move cases above)bugprone-command-processor: 3 → 1 (the ffmpeg one)fnv1aHash: I compiled the new function on its own against libmlx and compared it to a NumPy reference. It matches for a 3-D float32 array, a transposed non-contiguous view of it, and an int32 vector.posix_spawnpcode received"../my models/x; echo INJECTED"as a single argv entry. A missing binary takes the error branch.Not verified: macOS / Metal, linking and running the example binaries, and model inference. CI on this PR covers the Ubuntu and macOS builds.
🤖 Generated with Claude Code