txgsync's picture
download
raw
12.3 kB
# NanoSeedLM on macOS / Apple Silicon (Metal). Windows and Linux with NVIDIA CUDA: CMakeLists.txt (see README).
# make / make build metallibs into out/res, binaries into out/bin
# make test every test (test_affine's MLX goldens come from $(PY) tools/mova_affine_golden.py)
# make clean
#
# The command-line tools, the tokenizer and the tests are C, shared with the CUDA build; Objective-C remains only for
# the Metal engine (engine/mova_gpu.m), the Metal search glue, and the Metal-specific kernel benchmark and kernel test.
OUT := out
BIN := $(OUT)/bin
RES := $(OUT)/res
OBJ := $(OUT)/obj
CC := clang
CWARN := -Wall -Wextra -Wno-unused-function -Wno-unused-parameter
OBJCFLAGS := -O2 -g -fobjc-arc $(CWARN) -Iengine -Iharness -Inslm
# no FMA contraction: scalar, vector and GPU paths must agree bit for bit
CFLAGS := -std=c11 -O2 -g $(CWARN) -ffp-contract=off -Inslm -Iharness -Iengine
LIBS := -framework Metal -framework Foundation -framework CoreFoundation -framework IOKit
HLIBS := -licucore -framework CoreFoundation -framework IOKit
METAL := xcrun -sdk macosx metal -O3 -std=metal3.2
PY ?= python3
NSLM_LIB := $(wildcard nslm/lib_*.c)
NSLM_HDRS := $(wildcard nslm/*.h)
ENGINE := engine/mova_gpu.m nslm/lib_model_st.c nslm/lib_json.c nslm/lib_mova_cfg.c nslm/lib_mova_ckpt.c nslm/lib_moe.c nslm/lib_format.c nslm/lib_sha256.c
HARNESS := harness/platform.c harness/tokenizer.c harness/chat_template.c harness/chat_qwen.c harness/chat_model.c harness/tool_calls.c harness/kv_disk.c
HARN_HDRS := $(wildcard harness/*.h)
ENG_HDRS := engine/engine_api.h engine/mova_ext.h engine/kernels_moe.metal nslm/model_st.h nslm/json.h nslm/mova_cfg.h nslm/mova_ckpt.h nslm/lfsr.h \
engine/engine_mova.h engine/engine_qwen4.h engine/engine_api_fns.h engine/engine_unname.h engine/qwen4_eng.h
METALLIBS := $(RES)/kernels_moe.metallib $(RES)/kernels_qwen4.metallib $(RES)/search.metallib $(RES)/search4.metallib \
$(RES)/searchp.metallib
ENG_TOOLS := nslm-chat nslm-serve nslm-mova-smoke nslm-mova-gen nslm-mova-score nslm-mova-plcheck nslm-mova-refcheck nslm-mova-routes \
nslm-mova-mlacapture
TOOLS := $(addprefix $(BIN)/,$(ENG_TOOLS) nslm-mova-bench nslm-mova-kbench nslm-moe nslm-dense nslm-mova-pack nslm-qwen4-pack nslm-qwen4-emulate nslm-bits-probe nslm-cpu-seed-bench \
nslm-qwen4-gen)
C_TESTS := $(filter-out tests/test_engine.c tests/test_engine_formats.c tests/test_serve_splitter.c tests/test_search_gpu.c tests/test_search4_gpu.c tests/test_searchc_gpu.c tests/test_searchp_gpu.c tests/test_trellis_gpu.c \
tests/test_mova_kernels.c tests/test_qwen4_kernels.c \
tests/test_qwen4_engine.c tests/test_qwen4_api.c tests/test_qwen4_mtp.c tests/test_qwen4_api_mtp.c tests/test_engine_dispatch.c tests/test_qwen4_metal.c,$(wildcard tests/test_*.c))
TESTS := $(patsubst tests/%.c,$(BIN)/%,$(C_TESTS)) $(BIN)/test_engine $(BIN)/test_serve_splitter $(BIN)/test_search_gpu \
$(BIN)/test_search4_gpu $(BIN)/test_searchp_gpu $(BIN)/test_mova_kernels $(BIN)/test_qwen4_kernels $(BIN)/test_qwen4_metal \
$(BIN)/test_qwen4_mtp $(BIN)/test_qwen4_api_mtp
.PHONY: all build test test-mlx clean
all: build
build: $(METALLIBS) $(TOOLS)
# ---- Metal libraries (the search kernels without fast math: their f32 arithmetic must equal the CPU's) -----------
$(RES)/kernels_moe.metallib: engine/kernels_moe.metal
@mkdir -p $(@D)
$(METAL) -c $< -o $(@D)/kernels_moe.air && xcrun -sdk macosx metallib $(@D)/kernels_moe.air -o $@
# the qwen4 kernels without fast math: they match nslm/qwen4_ops.h within 1e-5 (RoPE at large positions, exp, rsqrt)
$(RES)/kernels_qwen4.metallib: engine/kernels_qwen4.metal
@mkdir -p $(@D)
$(METAL) -fno-fast-math -c $< -o $(@D)/kernels_qwen4.air && xcrun -sdk macosx metallib $(@D)/kernels_qwen4.air -o $@
$(RES)/search.metallib: nslm/search.metal nslm/search_gpu.h nslm/lfsr.h
@mkdir -p $(@D)
$(METAL) -fno-fast-math -Inslm -c $< -o $(@D)/search.air && xcrun -sdk macosx metallib $(@D)/search.air -o $@
$(RES)/search4.metallib: nslm/search4.metal nslm/search4_gpu.h nslm/lfsr.h
@mkdir -p $(@D)
$(METAL) -fno-fast-math -Inslm -c $< -o $(@D)/search4.air && xcrun -sdk macosx metallib $(@D)/search4.air -o $@
$(RES)/searchp.metallib: nslm/searchp.metal nslm/searchp_gpu.h nslm/lfsr.h
@mkdir -p $(@D)
$(METAL) -fno-fast-math -Inslm -c $< -o $(@D)/searchp.air && xcrun -sdk macosx metallib $(@D)/searchp.air -o $@
# ---- objects: the engine (Metal), the shared harness and the C library ---------------------------------------------
# engine_api.h for every model (option A): engine/engine.c picks the MoVA engine (engine/mova_gpu.m, its eng_* named
# mova_eng_* by engine_mova.h) or the qwen4 one (engine/qwen4_api.c over engine/qwen4_gpu.m, q4_eng_*) by config.json
MOVA_O := $(OBJ)/mova_gpu.o
$(MOVA_O): engine/mova_gpu.m $(ENG_HDRS)
@mkdir -p $(@D)
$(CC) $(OBJCFLAGS) -c $< -o $@
# the qwen4 Metal engine (engine/qwen4_eng.h): alone for nslm-qwen4-gen and test_qwen4_metal, else under qwen4_api.c
Q4E_O := $(OBJ)/qwen4_gpu.o
$(Q4E_O): engine/qwen4_gpu.m engine/qwen4_eng.h engine/kernels_moe.metal engine/kernels_qwen4.metal $(NSLM_HDRS)
@mkdir -p $(@D)
$(CC) $(OBJCFLAGS) -c $< -o $@
$(OBJ)/e_%.o: engine/%.c $(ENG_HDRS) $(NSLM_HDRS)
@mkdir -p $(@D)
$(CC) $(CFLAGS) -c $< -o $@
ENG_O := $(MOVA_O) $(OBJ)/e_engine.o $(OBJ)/e_qwen4_api.o $(Q4E_O)
HARN_O := $(patsubst harness/%.c,$(OBJ)/h_%.o,$(HARNESS))
$(OBJ)/h_%.o: harness/%.c $(HARN_HDRS) nslm/json.h
@mkdir -p $(@D)
$(CC) $(CFLAGS) -c $< -o $@
LIB_O := $(patsubst nslm/%.c,$(OBJ)/n_%.o,$(NSLM_LIB))
$(OBJ)/n_%.o: nslm/%.c $(NSLM_HDRS)
@mkdir -p $(@D)
$(CC) $(CFLAGS) -O3 -c $< -o $@
TELEM_O := $(OBJ)/telemetry.o
$(TELEM_O): harness/telemetry.m harness/telemetry.h
@mkdir -p $(@D)
$(CC) $(OBJCFLAGS) -c $< -o $@
# ---- tools ----------------------------------------------------------------------------------------------------------
$(addprefix $(BIN)/,$(ENG_TOOLS)): $(BIN)/%: harness/%.c $(ENG_O) $(HARN_O) $(LIB_O) $(HARN_HDRS) $(ENG_HDRS)
@mkdir -p $(@D)
$(CC) $(CFLAGS) $< $(ENG_O) $(HARN_O) $(LIB_O) $(LIBS) $(HLIBS) -lpthread -o $@
$(BIN)/nslm-mova-bench: harness/nslm-mova-bench.c $(ENG_O) $(HARN_O) $(LIB_O) $(TELEM_O) $(HARN_HDRS) $(ENG_HDRS)
@mkdir -p $(@D)
$(CC) $(CFLAGS) $< $(ENG_O) $(HARN_O) $(LIB_O) $(TELEM_O) $(LIBS) $(HLIBS) -o $@
$(BIN)/nslm-mova-kbench: harness/nslm-mova-kbench.m harness/common.h engine/kernels_moe.metal nslm/lfsr.h
@mkdir -p $(@D)
$(CC) $(OBJCFLAGS) harness/nslm-mova-kbench.m $(LIBS) -o $@
# ---- compressor: seed search (C + the Metal search glue) and the packer -------------------------------------------
SEARCH_O := $(OBJ)/search_metal.o $(OBJ)/search4_metal.o $(OBJ)/searchc_metal.o $(OBJ)/searchp_metal.o $(OBJ)/n_trellis_none.o
$(OBJ)/%_metal.o: nslm/%_metal.m $(NSLM_HDRS)
@mkdir -p $(@D)
$(CC) $(OBJCFLAGS) -c $< -o $@
$(BIN)/nslm-moe: nslm/moe.c $(SEARCH_O) $(LIB_O) $(NSLM_HDRS)
@mkdir -p $(@D)
$(CC) $(CFLAGS) -O3 nslm/moe.c $(SEARCH_O) $(LIB_O) -o $@ -lm -lpthread $(LIBS)
$(BIN)/nslm-dense: nslm/dense.c $(OBJ)/search4_metal.o $(OBJ)/searchp_metal.o $(LIB_O) $(NSLM_HDRS)
@mkdir -p $(@D)
$(CC) $(CFLAGS) -O3 nslm/dense.c $(OBJ)/search4_metal.o $(OBJ)/searchp_metal.o $(LIB_O) -o $@ -lm -lpthread $(LIBS)
$(BIN)/nslm-mova-pack: nslm/mova_pack.c $(LIB_O) $(NSLM_HDRS)
@mkdir -p $(@D)
$(CC) $(CFLAGS) -O3 $< $(LIB_O) -o $@ -lm -lpthread
$(BIN)/nslm-bits-probe: nslm/bits_probe.c $(LIB_O) $(NSLM_HDRS)
@mkdir -p $(@D)
$(CC) $(CFLAGS) -O3 $< $(LIB_O) -o $@ -lm -lpthread
$(BIN)/nslm-cpu-seed-bench: harness/nslm-cpu-seed-bench.c $(LIB_O) $(NSLM_HDRS)
@mkdir -p $(@D)
$(CC) $(CFLAGS) -O3 $< $(LIB_O) -o $@ -lm -lpthread
$(BIN)/nslm-qwen4-pack: nslm/qwen4_pack.c $(LIB_O) $(NSLM_HDRS)
@mkdir -p $(@D)
$(CC) $(CFLAGS) -O3 $< $(LIB_O) -o $@ -lm -lpthread
$(BIN)/nslm-qwen4-emulate: nslm/qwen4_emulate.c $(LIB_O) $(NSLM_HDRS)
@mkdir -p $(@D)
$(CC) $(CFLAGS) -O3 $< $(LIB_O) -o $@ -lm -lpthread
# ---- tests (the same C sources as the CUDA build; test_mova_kernels runs on tests/kernel_backend_metal.m) ---------
$(BIN)/test_%: tests/test_%.c $(LIB_O) $(NSLM_HDRS)
@mkdir -p $(@D)
$(CC) $(CFLAGS) $< $(LIB_O) -o $@ -lm -lpthread
$(BIN)/test_chat_template $(BIN)/test_tool_calls $(BIN)/test_tokenizer $(BIN)/test_kv_disk $(BIN)/test_qwen4_template $(BIN)/test_qwen4_tools: $(BIN)/test_%: tests/test_%.c $(HARN_O) $(LIB_O) tests/data/template_golden.json
@mkdir -p $(@D)
$(CC) $(CFLAGS) $< $(HARN_O) $(LIB_O) $(HLIBS) -o $@ -lm
$(BIN)/test_engine: tests/test_engine.c $(ENG_O) $(LIB_O) $(ENG_HDRS) $(RES)/kernels_moe.metallib
@mkdir -p $(@D)
$(CC) $(CFLAGS) $< $(ENG_O) $(LIB_O) $(LIBS) -lpthread -o $@
$(BIN)/test_serve_splitter: tests/test_serve_splitter.c harness/nslm-serve.c $(ENG_O) $(HARN_O) $(LIB_O) $(HARN_HDRS) $(ENG_HDRS)
@mkdir -p $(@D)
$(CC) $(CFLAGS) $< $(ENG_O) $(HARN_O) $(LIB_O) $(LIBS) $(HLIBS) -lpthread -o $@
$(BIN)/test_search_gpu: tests/test_search_gpu.c $(OBJ)/search_metal.o $(LIB_O) $(NSLM_HDRS) $(RES)/search.metallib
@mkdir -p $(@D)
$(CC) $(CFLAGS) $< $(OBJ)/search_metal.o $(LIB_O) $(LIBS) -lpthread -o $@
$(BIN)/test_searchp_gpu: tests/test_searchp_gpu.c $(OBJ)/searchp_metal.o $(LIB_O) $(NSLM_HDRS) $(RES)/searchp.metallib
@mkdir -p $(@D)
$(CC) $(CFLAGS) $< $(OBJ)/searchp_metal.o $(LIB_O) $(LIBS) -lpthread -o $@
$(BIN)/test_search4_gpu: tests/test_search4_gpu.c $(OBJ)/search4_metal.o $(LIB_O) $(NSLM_HDRS) $(RES)/search4.metallib
@mkdir -p $(@D)
$(CC) $(CFLAGS) $< $(OBJ)/search4_metal.o $(LIB_O) $(LIBS) -lpthread -o $@
$(BIN)/test_mova_kernels: tests/test_mova_kernels.c tests/kernel_backend_metal.m tests/kernel_backend.h engine/kernels_moe.metal \
engine/kernels_qwen4.metal $(LIB_O) $(RES)/kernels_moe.metallib
@mkdir -p $(@D)
$(CC) $(CFLAGS) -c tests/test_mova_kernels.c -o $(OBJ)/test_mova_kernels.o
$(CC) $(OBJCFLAGS) -Itests tests/kernel_backend_metal.m $(OBJ)/test_mova_kernels.o $(LIB_O) $(LIBS) -o $@
$(BIN)/test_qwen4_kernels: tests/test_qwen4_kernels.c tests/kernel_backend_metal.m tests/kernel_backend.h engine/kernels_moe.metal \
engine/kernels_qwen4.metal $(LIB_O) $(RES)/kernels_moe.metallib $(RES)/kernels_qwen4.metallib
@mkdir -p $(@D)
$(CC) $(CFLAGS) -c tests/test_qwen4_kernels.c -o $(OBJ)/test_qwen4_kernels.o
$(CC) $(OBJCFLAGS) -Itests tests/kernel_backend_metal.m $(OBJ)/test_qwen4_kernels.o $(LIB_O) $(LIBS) -o $@
$(BIN)/test_qwen4_metal: tests/test_qwen4_metal.c $(Q4E_O) $(LIB_O) $(NSLM_HDRS) $(RES)/kernels_moe.metallib $(RES)/kernels_qwen4.metallib
@mkdir -p $(@D)
$(CC) $(CFLAGS) $< $(Q4E_O) $(LIB_O) $(LIBS) -lm -lpthread -o $@
# the MTP head on the Metal engine: the same test as the CUDA build's
$(BIN)/test_qwen4_mtp: tests/test_qwen4_mtp.c $(Q4E_O) $(LIB_O) $(NSLM_HDRS) $(RES)/kernels_moe.metallib $(RES)/kernels_qwen4.metallib
@mkdir -p $(@D)
$(CC) $(CFLAGS) $< $(Q4E_O) $(LIB_O) $(LIBS) -lm -lpthread -o $@
# eng_generate with MTP drafts (Q4E_DRAFTS) through engine/engine.c: the same test as the CUDA build's
$(BIN)/test_qwen4_api_mtp: tests/test_qwen4_api_mtp.c $(ENG_O) $(LIB_O) $(ENG_HDRS) $(NSLM_HDRS) $(RES)/kernels_moe.metallib $(RES)/kernels_qwen4.metallib
@mkdir -p $(@D)
$(CC) $(CFLAGS) $< $(ENG_O) $(LIB_O) $(LIBS) -lm -lpthread -o $@
$(BIN)/nslm-qwen4-gen: harness/nslm-qwen4-gen.c $(Q4E_O) $(HARN_O) $(LIB_O) $(HARN_HDRS) $(NSLM_HDRS)
@mkdir -p $(@D)
$(CC) $(CFLAGS) $< $(Q4E_O) $(HARN_O) $(LIB_O) $(LIBS) $(HLIBS) -lm -lpthread -o $@
$(OUT)/test/affine/index.txt: tools/mova_affine_golden.py tools/mova_common.py
$(PY) tools/mova_affine_golden.py --out $(OUT)/test/affine
# the MLX loader (tools/nanoseedlm_k2.py); OMLX_K2_MODEL = oMLX's k2_horizon_model.py
test-mlx:
$(PY) tools/test_nanoseedlm_k2.py
# the MLX goldens of test_affine are built first (macOS has MLX); test_mova_cfg and test_tokenizer read MOVA_DIR (a model
# folder) and are skipped without one. A test that exits 77 is skipped.
test: $(TESTS) $(OUT)/test/affine/index.txt
@for t in $(TESTS); do echo "== $$t"; $$t; r=$$?; if [ $$r -eq 77 ]; then echo "(skipped)"; elif [ $$r -ne 0 ]; then exit 1; fi; done; echo "all tests passed"
clean:
rm -rf $(OUT)

Xet Storage Details

Size:
12.3 kB
·
Xet hash:
53afe66c8315cf0afc9ed00820ba9c42c2278004a02f9163b992904bc7d8006d

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.