Skip to content

fix and feature: clean MVMextractmode#381

Merged
DasVinch merged 15 commits into
framework-devfrom
vd/clean_mvm
Jul 22, 2026
Merged

fix and feature: clean MVMextractmode#381
DasVinch merged 15 commits into
framework-devfrom
vd/clean_mvm

Conversation

@DasVinch

@DasVinch DasVinch commented Jun 2, 2026

Copy link
Copy Markdown
Member

THIS HAS BEEN REBASE AND IS MERGEABLE (2026-07-22)

I've rebased the entire thing - its now an overwrite of the actual code in linearalgebra and is ready to merge.

MVM -> C++
Capabilities: CPU-plain, BLAS, CUDA CPU-ordered, CUDA with stream.
Building on GPUindex 99 for CPU:

  • 99 -> goes to BLAS or fallback to 98
  • 98 -> plain CPU with OPENMP if possible (e.g. forcing no BLAS)
  • n with n >= 0 --> GPU with CPU synchronization
  • n with n < 0 --> GPU (-n-1) in CUDA stream mode

Minor updates to streamDelay still there.

Stubs for a pytest test suite.
For now its a bit of "it runs on my machine (TM)" since the suite is entirely dependent on the latest pyMilk developments.
And the tests of interest here are not integrated in an auto test-suite, but bundled in the tests.

FYI - I run them with

pytest -x -s -v tests/mains/experiment_deploy.py -k TestsAxmode

for future ref if anyone reads this:

  • -s prints stdout
  • -x fails at first failure
  • -v verbose
  • -k only run the classes of tests matching "TestsAxmode" (ie, the MVM tests)

THIS IS A DEMO PR WITH NO INTENTION TO MERGE AS AS

This PR is a re-write of MVMextractModes.c that showcases a number of interesting approaches for future development.
Summary of changes:

  • Refactor linalgebra/MVMextractModes.c to finish removing unused or irrelevant attributes. Sanitize a few segfaults, remove references to fetching images into milk-data by ImageID. [must go to upstream]

  • Remove test file that was not actually testing our production code [upstream]

  • There's also some fixing to streamDelay.c that got bundled here through a few merges.

  • Create a separate module linalgebra2 really just for linalgebra2/MVMextractModes.cpp as a workspace

  • Create a test environment adapted to pytest used in conjuction with pyMilk under /testing

What I showcase

C++

  • We can introduce C++ code TUs in a (strong) C environment. That needed ~5 changes to the rest of the codebase (restrict / __restrict and (char*) forceful string literal casts)
  • Functionalization and a moderate amount of object-oriented C++ greatly helps readability of large functions, and not getting lost in long if/else block.
  • Calculations are displaced to a secondary file. API contracts are clear: mask is loaded, matrix is loaded, and the function backend::matrixMul() has a clear API contract: take data from the float-casted input array and write it to the output stream. See mvm_auxiliaries.hpp|cpp
  • Functions that are extracted, some of which are purely technical in nature e.g. the cuda initialization, can be library-fied for better reuse.
  • All data in C++ object can be arranged to be de'alloced automatically (that's the unique_ptr). Since the logic is in the destructor, when arguments are optionally used / optionally nullptr, it's much easier to keep track.
  • The rewrite helped to see that the current MVMextract was not abiding by it's arguments very well, and not consistently (masking = 1 + axmode = 1, etc)

TEST SUITE

  • testing/ is configured as a python module, for tests. testing/tests is a pytest suite (that contains nothing meaningful but the layout) that can be run with /testing>$ pytest
  • testing/tests/mains files are not executed by default, but pytest can be used to invoke them and run dev sessions. This is what I did for now, in a single file experiment_testing.
  • Using parametrization, it's easy to run the same test function many times with different options
  • Using fixtures, it's easy to cause changes to the environment, e.g. spoofing MILK_SHM_DIR during the tests
  • Using fixtures, it's easy to offer reproducible / recyclable setup/teardown codes to a number of tests. Here I use it to serve the FPS for the MVM to 72 different executions of the function
def mvm_correctness_all_params(
    fps_m: FPS,
    axmode: int,
    normalize: bool,
    GPUindex: int,
    masking: bool,
    input_dtype: np.typing.DTypeLike,
):
  • Finally, this has allowed me to 1/ check that the new MVM in cpp is correct compared to a very concise python implementation for all combinations of axmode, normalization, gpu/cpu/OPENMP/BLAS, input dtype, masking.
  • and that the existing MVM is correct at least for axmode 0, BLAS/GPU, masking, normalization
  • and get some timing printouts:
>$ # LINALG_ORIGINAL CPU
>$ pytest -sx tests/mains/experiment_deploy.py -k 'test_mvm_correctness[False-99-False] and TestsAxmode0' | grep ELA
ELAPSED [MODE EXTRACT] [GPUindex=99  normalize=0     masking=0     input_dtype=float32 ] 1477.60 ms
>$ # LINALG_ORIGINAL GPU
>$ pytest -sx tests/mains/experiment_deploy.py -k 'test_mvm_correctness[False-0-False] and TestsAxmode0' | grep ELA
ELAPSED [MODE EXTRACT] [GPUindex=0   normalize=0     masking=0     input_dtype=float32 ] 507.18 ms
>$ # LINALG_NEW CPU
>$ pytest -sx tests/mains/experiment_deploy.py -k 'test_mvm_correctness[False-99-False] and TestsAxmode0' | grep ELA
ELAPSED [MODE EXTRACT] [GPUindex=99  normalize=0     masking=0     input_dtype=float32 ] 1006.73 ms
>$ # LINALG_NEW GPU
>$ pytest -sx tests/mains/experiment_deploy.py -k 'test_mvm_correctness[False-0-False] and TestsAxmode0' | grep ELA
ELAPSED [MODE EXTRACT] [GPUindex=0   normalize=0     masking=0     input_dtype=float32 ] 499.78 ms
>$ # LINALG_ORIGINAL GPU MASKED
>$ pytest -sx tests/mains/experiment_deploy.py -k 'test_mvm_correctness[True-0-False] and TestsAxmode0' | grep ELA
ELAPSED [MODE EXTRACT] [GPUindex=0   normalize=0     masking=1     input_dtype=float32 ] 292.73 ms
>$ # LINALG_NEW GPU MASKED
>$ pytest -sx tests/mains/experiment_deploy.py -k 'test_mvm_correctness[True-0-False] and TestsAxmode0' | grep ELA
ELAPSED [MODE EXTRACT] [GPUindex=0   normalize=0     masking=1     input_dtype=float32 ] 296.04 ms

Timings 1/3 better on CPU (mostly because masking got factored in), a few % on GPU.

What's not great (in this impementation of the MVM): error checking.

Nits:

  • Re-added Ctrl+A in fpsCTRL to attach tmux session.
  • Many string buffers for printing need increases in size to get rid of compiler warnings. I just touched one.

@DasVinch
DasVinch force-pushed the vd/clean_mvm branch 2 times, most recently from d72e15d to e813048 Compare July 22, 2026 08:39
@DasVinch

Copy link
Copy Markdown
Member Author

I've rebased the entire thing - its now an overwrite of the actual code in linearalgebra and is ready to merge.

@DasVinch
DasVinch marked this pull request as ready for review July 22, 2026 09:46
Copilot AI review requested due to automatic review settings July 22, 2026 09:46
@DasVinch
DasVinch merged commit 6b22ddd into framework-dev Jul 22, 2026
3 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

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 modernizes the linalgebra MVM mode extraction/expansion compute unit by rewriting MVMextractModes in C++ with pluggable backends (CPU/OMP, BLAS, CUDA/cuBLAS, CUDA-stream/graph), while also adding a new testing/ Python harness (pytest + nox) intended to support coverage and future correctness testing.

Changes:

  • Replaces plugins/milk-extra-src/linalgebra/MVMextractModes.c with a new C++ implementation (MVMextractModes.cpp) plus backend helpers (mvm_auxiliaries.*).
  • Adds a testing/ package containing pytest fixtures, placeholder tests, and a nox session for coverage workflows.
  • Minor infrastructure updates (CMake C++ standard flags, .clang-format, BLAS/LAPACKE header notes, fps.h PATH_MAX usage).

Reviewed changes

Copilot reviewed 23 out of 27 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
testing/tests/trivial/first_test.py Adds a trivial pytest to exercise a milk binary (currently needs to be made robust for CI).
testing/tests/trivial/init.py Initializes trivial test package.
testing/tests/mains/experiment_deploy.py Adds a heavy/manual pytest “main” experiment script for streamDelay + MVM benchmarking/correctness.
testing/tests/mains/init.py Initializes mains test package.
testing/tests/conftestaux/milk.py Adds autouse session fixture to spoof MILK_SHM_DIR/MILK_PROC_DIR for tests.
testing/tests/conftestaux/coverage.py Adds session-end gcov dump fixture (if pyMilk wrapper supports it).
testing/tests/conftestaux/cacao_loop.py Placeholder for future cacao loop fixtures.
testing/tests/conftestaux/init.py Initializes conftestaux package.
testing/tests/conftest.py Registers pytest plugin fixtures and adds cwd-to-temp autouse fixture.
testing/tests/conftest_test.py Adds sanity checks that fixtures actually ran.
testing/tests/init.py Initializes tests package.
testing/setup.py Adds a setuptools+CMake-based build wrapper for the testing package.
testing/pyproject.toml Adds pytest config and pyright config for the testing package (ignore pattern currently incorrect).
testing/noxfile.py Adds nox automation for building coverage and running pytest/gcovr.
testing/lazymake.sh Convenience script for local build/copy of shared libs (dev helper).
testing/clean.sh Convenience cleanup script for local pyMilk builds (dev helper).
src/engine/libmilkcommon/milk_blas_lapacke.h Adds BLAS_LIB string macro + C++ include note for LAPACKE header usage.
src/engine/libfps/fps.h Adds PATH_MAX include and uses PATH_MAX for tmux executable path buffer sizing.
plugins/milk-extra-src/linalgebra/MVMextractModes.hpp New header for CLI registration symbol (currently has a C++ guard typo).
plugins/milk-extra-src/linalgebra/MVMextractModes.cpp New C++ FPS compute unit implementation with runtime backend selection.
plugins/milk-extra-src/linalgebra/MVMextractModes.c Removes the prior C implementation.
plugins/milk-extra-src/linalgebra/mvm_auxiliaries.hpp Adds backend class interfaces for CPU/BLAS/CUDA/cuBLAS/CUDA-graph.
plugins/milk-extra-src/linalgebra/mvm_auxiliaries.cpp Adds backend implementations (CPU/OMP, BLAS sgemv, cuBLAS, CUDA graph).
plugins/milk-extra-src/linalgebra/linalgebra.c Removes a weak “test” CLI hook registration.
plugins/milk-extra-src/linalgebra/CMakeLists.txt Expands module sources to include .cpp/.hpp and switches standalone to build from .cpp.
CMakeLists.txt Sets C++ standard flags to -std=c++17 (instead of -std=gnu17).
.clang-format Adds IndentExternBlock: NoIndent to improve formatting around extern "C".

Comment on lines +9 to +12
#ifdef __cpluscplus
extern "C"
{
#endif
Comment on lines +15 to +17
#ifdef __cpluscplus
}
#endif
Comment on lines +409 to +413
if (backend == nullptr)
{
// TODO print a very explicit error message and fail
// enum error? exit(0)
}
Comment on lines +455 to +459
/* Type conversion to float (all backends) */
if (imgid_in.md->datatype != _DATATYPE_FLOAT)
{
cast_to_float(imgin_float_casted_ptr, imgid_in.im, n_pixels_spatial_side);
}
Comment on lines +461 to +466
imgid_out.md->write =
1; // We don't really know at which point the backend workflow will begin writes, so we flag early.

backend->matrixMul(); // Here we MVM
// We're done
processinfo_update_output_stream(processinfo, imgid_out.im, imgid_in.im);
Comment thread testing/pyproject.toml
Comment on lines +24 to +27
addopts = [
"--import-mode=importlib",
"--ignore=mains/*",
]
Comment on lines +8 to +14
import subprocess


def test_a_trivial_thing():
subprocess.run(["milk-fps-list"])

assert True
Comment thread src/engine/libfps/fps.h
#ifndef FPS_H
#define FPS_H

#include <linux/limits.h> // For PATH_MAX (but maybe would belong in a global milk compilation header?) // For some reason this fails only for standalone execs, and even not all of them!!
@DasVinch
DasVinch deleted the vd/clean_mvm branch July 22, 2026 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants