Repository navigation
Expand file tree
/
Copy pathCMakeLists.txt
More file actions
2794 lines (2698 loc) · 194 KB
/
Copy pathCMakeLists.txt
File metadata and controls
2794 lines (2698 loc) · 194 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Strata Engine - top-level build (P1.S1).
#
# Targets (phase-1 §P1.S1): `strata_artifact` (no CUDA dependency), `tests`. `strata_plan` and the C++
# `strata-pack` are added when their sources exist - a target with no sources would make the build graph
# claim something the tree does not have, which is worse than an absent target.
#
# CUDA targets arrive in Phase 2. The kernels need sm_80 or newer (tf32 mma, bf16 math) and the guard
# below refuses anything older: silently building for a pre-Ampere arch would produce a binary that runs
# and is wrong about its own performance, which is the worst of both outcomes.
cmake_minimum_required(VERSION 3.24)
project(strata VERSION 0.1.55 LANGUAGES CXX)
# EXPERIMENTAL: the Intel Arc engine, the SYCL port in sycl/ (PR #423; icpx + oneMKL, docs/INTEL_ARC.md). It is its
# own project that takes the unported sources from src/; with this on, nothing below is configured.
option(STRATA_ENABLE_SYCL "EXPERIMENTAL: build the Intel Arc (SYCL) engine from sycl/ - docs/INTEL_ARC.md" OFF)
if(STRATA_ENABLE_SYCL)
if(STRATA_ENABLE_CUDA OR STRATA_ENABLE_HIP)
message(FATAL_ERROR "STRATA_ENABLE_SYCL is a separate backend; leave STRATA_ENABLE_CUDA and STRATA_ENABLE_HIP off")
endif()
message(WARNING "STRATA_ENABLE_SYCL: the Intel Arc engine is experimental (community port, docs/INTEL_ARC.md)")
add_subdirectory(sycl)
return()
endif()
add_compile_definitions(STRATA_VERSION="${PROJECT_VERSION}")
# issue #31: release builds keep a PDB beside the exe (not shipped in it), so a minidump from a stalled engine can
# be read back to the exact source line
if(MSVC AND CMAKE_BUILD_TYPE STREQUAL "Release")
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/Zi>)
add_link_options(/DEBUG /OPT:REF /OPT:ICF)
endif()
# The prebuilt release (setup.py downloads it, so users need no compiler): ggml's CPU backend for any AVX2 CPU
# instead of this machine's (Strata's own AVX-512 kernels are per-file and chosen at run time either way), and
# the MSVC runtime linked in, so no Visual C++ redistributable is needed.
option(STRATA_PORTABLE "build for other PCs: AVX2 baseline, static MSVC runtime" OFF)
# EXPERIMENTAL, opt-in: an engine for CPUs without AVX2 (#394, #595, #623), compiled on that PC. ggml-cpu, which then
# computes every CPU expert row, is built for this floor instead of AVX2 or the build host: "avx" (Sandy/Ivy Bridge:
# AVX, no FMA/F16C/AVX2) or "none" (SSE4.2 + POPCNT: Nehalem/Westmere). Strata's own AVX2/AVX-512 kernels stay in the
# binary behind their CPU checks; the router lookahead gets an AVX1 kernel. Empty (the default) changes nothing.
set(STRATA_ISA_FLOOR "" CACHE STRING "experimental older-CPU build: empty (default, AVX2 floor), avx, or none (SSE4.2)")
set_property(CACHE STRATA_ISA_FLOOR PROPERTY STRINGS "" avx none)
if(NOT STRATA_ISA_FLOOR STREQUAL "" AND NOT STRATA_ISA_FLOOR STREQUAL "avx" AND NOT STRATA_ISA_FLOOR STREQUAL "none")
message(FATAL_ERROR "STRATA_ISA_FLOOR must be empty, avx or none (got '${STRATA_ISA_FLOOR}')")
endif()
if(STRATA_ISA_FLOOR AND STRATA_PORTABLE)
message(FATAL_ERROR "STRATA_ISA_FLOOR (an older-CPU build) and STRATA_PORTABLE (the AVX2 release build) exclude each other")
endif()
if(STRATA_PORTABLE AND MSVC)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded")
endif()
# #3202: Windows hands a program its command line in the system's ANSI code page (cp1251 / cp866 on a Russian Windows, cp1252 on an
# English one), but the engine reads every path as UTF-8 (the run configs and the server write UTF-8). A model folder with
# Cyrillic letters arrived as other bytes, or as "?" where the page has no such letter, and the model did not open. This manifest
# makes the process's ANSI code page UTF-8 (Windows 10 1903 and newer; older Windows ignores it and nothing changes), so the
# command line, fopen and std::ifstream all take UTF-8.
function(strata_utf8_manifest target)
if(MSVC)
target_sources(${target} PRIVATE ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/cmake/strata-utf8.manifest)
endif()
endfunction()
# The AVX kernels exist for x86 only. On aarch64 (DGX Spark / GB10) portable.cpp stands in for them and
# native experts run on ggml-cpu's NEON dot products.
if(NOT CMAKE_SYSTEM_PROCESSOR OR CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64|x64|X64|i[3-6]86|x86|X86)$") # an unset processor stays the x86 build, as before
set(STRATA_CPU_X86 ON)
else()
set(STRATA_CPU_X86 OFF)
endif()
if(STRATA_ISA_FLOOR AND NOT STRATA_CPU_X86)
message(WARNING "STRATA_ISA_FLOOR is an x86 older-CPU build and does not apply to ${CMAKE_SYSTEM_PROCESSOR}; ignored")
set(STRATA_ISA_FLOOR "" CACHE STRING "" FORCE)
endif()
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
endif()
option(STRATA_ENABLE_CUDA "Build the CUDA targets (Phase 2+)" OFF)
# AMD gfx906 (MI50, wave64): the same .cu sources compiled as HIP through the gfx906 compat layer. The CUDA runtime and
# cuBLAS names are mapped by the headers in include/strata/platform/hip_compat/, which go first on the include path,
# and CUDA::cudart / CUDA::cublas become aliases of HIP's targets, so every CUDA target below stays as it is. This is
# separate from STRATA_ENABLE_HIP (the wave32 RDNA backend): with it on, STRATA_ENABLE_CUDA is on and
# STRATA_ENABLE_HIP stays off, so every "CUDA" branch of the tree is the one taken.
option(STRATA_HIP_GFX906 "Build the CUDA targets for AMD gfx906 through HIP (wave64 compat layer)" OFF)
if(STRATA_HIP_GFX906)
if(STRATA_ENABLE_HIP)
message(FATAL_ERROR "STRATA_HIP_GFX906 and STRATA_ENABLE_HIP are separate backends; enable only one")
endif()
set(STRATA_ENABLE_CUDA ON CACHE BOOL "" FORCE)
endif()
option(STRATA_ENABLE_HIP "Build the HIP targets for wave32 AMD GPUs (gfx1100, gfx1201; gfx1012/gfx1030 unvalidated)" OFF)
option(STRATA_PREFILL_MMQ "Build the optional GGML MMQ prefill path for HIP" OFF)
# The rows one verify / batch window holds (include/strata/kernels/window_limits.hpp): 8, the 0.2.7 window, or 16 (four pipelined
# groups of 16 slots = 64 slots on a four-card split, docs/BATCHING.md). Only the CUDA and HIP engines are built from
# these sources with a wide window; the SYCL and Metal builds never read this variable and keep 8. A window of up to
# 8 rows runs the kernels it always ran, whichever is chosen (the wide instantiations are extra code). "auto" = 16 for
# the HIP engines (a HIP code object loads on first use, so the extra kernels cost nothing until a wide window forms)
# and 8 for CUDA, which loads every kernel at start (CUDA_MODULE_LOADING=EAGER: the wide kernels would take VRAM from
# the expert cache of every card); -DSTRATA_WINDOW_MAX_T=16 builds the wide CUDA engine.
set(STRATA_WINDOW_MAX_T "auto" CACHE STRING "rows of a verify / batch window: 8, 16 or auto (HIP 16, CUDA 8)")
set_property(CACHE STRATA_WINDOW_MAX_T PROPERTY STRINGS auto 8 16)
if(NOT STRATA_WINDOW_MAX_T STREQUAL "auto" AND NOT STRATA_WINDOW_MAX_T STREQUAL "8" AND NOT STRATA_WINDOW_MAX_T STREQUAL "16")
message(FATAL_ERROR "STRATA_WINDOW_MAX_T must be auto, 8 or 16 (got '${STRATA_WINDOW_MAX_T}')")
endif()
if(STRATA_ENABLE_HIP OR STRATA_ENABLE_CUDA)
if(STRATA_WINDOW_MAX_T STREQUAL "auto")
if(STRATA_ENABLE_HIP)
set(_strata_window_max_t 16)
else()
set(_strata_window_max_t 8)
endif()
else()
set(_strata_window_max_t ${STRATA_WINDOW_MAX_T})
endif()
add_compile_definitions(STRATA_VERIFY_MAX_T=${_strata_window_max_t})
message(STATUS "Strata: verify / batch windows hold up to ${_strata_window_max_t} rows")
endif()
# MMQ prompt kernels for Q4_K / Q5_K / Q5_1 / Q6_K (Unsloth's UD-Q4/Q5/Q6_K_XL experts; on HIP also the dense GGUF
# projections, STRATA_DENSE_MMQ). On by default since MODEL-UPDATE Phase 1: on CUDA they are their own library
# (strata_kq, loaded only for a model with these formats: the engine loads every kernel of its own binary at start,
# CUDA_MODULE_LOADING=EAGER, and these took ~3.8 MB of VRAM - 3 IQ3_XXS expert slots on a P100 - from every model); on
# HIP a code object loads on first use, so they cost other models nothing there (R9700: the same slots). Used at run
# time with STRATA_MMQ_KQ=1 (opt-in: it changes the prompt's rounding against the FP16 path).
option(STRATA_MMQ_TQ_OVERLAY "Gyro trellis MMQ prompt kernels on an upstream llama.cpp: an overlay of its ggml-cuda headers written at configure time (cmake/strata_ggml_tq_overlay.cmake)" ON)
option(STRATA_MMQ_KQUANTS "Build the MMQ prompt kernels for Q4_K / Q5_K / Q5_1 / Q6_K: the experts of Unsloth's K-quant files, and on HIP the dense GGUF projections (STRATA_DENSE_MMQ)" ON)
# Q6_K gate/up in the grouped expert kernels (Unsloth's UD-Q6_K_XL, and layer 2 of UD-Q5_K_XL; docs/QUANTS.md). On by
# default since MODEL-UPDATE Phase 1: one more instance per grouped kernel in iq_kernels.cu, +19 KB of sm_60 code,
# measured to leave every expert-cache slot count as it was (P100, R9700). -DSTRATA_Q6K_EXPERTS=OFF drops it.
option(STRATA_Q6K_EXPERTS "Build the Q6_K gate/up grouped expert kernels (UD-Q6_K_XL, UD-Q5_K_XL)" ON)
if(STRATA_Q6K_EXPERTS)
add_compile_definitions(STRATA_Q6K_EXPERTS=1)
endif()
option(STRATA_ORCA_Q4KS_MMQ "Build the GGML Q5_0 MMQ instance for OrcaRouter Q4_K_S" OFF)
# CUDA: the pointer-table MMQ behind STRATA_MMQ_DIRECT=1 (the prompt path's MMQ reads resident experts in their cache
# slots through a Strata-side copy of llama.cpp's MMQ scheduler, src/prefill/moe_mmq_tab.cuh). Off by default, like
# the K-quant options above: the engine loads every kernel when it starts (CUDA_MODULE_LOADING=EAGER), and these nine
# instances (+15 MB of binary) take 36-38 MiB of VRAM per GPU on sm_86 - expert slots every model would lose, with
# STRATA_MMQ_DIRECT unset too. They also add ~70% of the upstream MMQ instances' compile time per CUDA arch (175 s
# of CPU for the nine at sm_86 alone). Off, the build's device code is byte for byte the one without this feature. CUDA
# only (not HIP, not gfx906); switched off at configure time when the llama.cpp tree's mmq.cuh is not one the copy
# was verified against.
option(STRATA_MMQ_TAB "Build the pointer-table prompt MMQ for STRATA_MMQ_DIRECT=1 (CUDA; ~37 MiB VRAM per GPU)" OFF)
if(STRATA_PREFILL_MMQ AND NOT STRATA_ENABLE_HIP)
message(FATAL_ERROR "STRATA_PREFILL_MMQ requires STRATA_ENABLE_HIP=ON")
endif()
if(STRATA_ENABLE_CUDA AND STRATA_ENABLE_HIP AND NOT STRATA_HIP_GFX906)
message(FATAL_ERROR "STRATA_ENABLE_CUDA and STRATA_ENABLE_HIP are separate backends; enable only one")
endif()
if(STRATA_ENABLE_HIP)
include(cmake/hip_backend.cmake)
endif()
# The test suite and the micro benchmarks live in tests/ and bench/micro/, which the published source leaves out.
set(_strata_tests_default OFF)
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/bench/micro)
set(_strata_tests_default ON)
endif()
if(NOT STRATA_CPU_X86)
# arm64 Linux (DGX Spark): much of the suite drives the x86 CPU kernels, which this build does not have. A default, not a
# prohibition: -DSTRATA_BUILD_TESTS=ON still builds what builds (classifying the tests by capability is a follow-up).
set(_strata_tests_default OFF)
endif()
option(STRATA_BUILD_TESTS "Build the test suite" ${_strata_tests_default})
option(STRATA_BUILD_CONVERSATION_TESTS "Build standalone conversation cache tests" OFF)
option(STRATA_WERROR "Treat compiler warnings as errors" OFF)
# #2027: defined here, before the GPU section reads it to register the native-expert tests (it was defined at the
# native-experts section far below, so a first configure registered four tests fewer than the second)
option(STRATA_NATIVE_EXPERTS "Native (IQ) expert support through ggml-cpu" ON)
if(STRATA_BUILD_CONVERSATION_TESTS)
enable_testing()
add_executable(conversation_cache_test src/core/conversation_cache_test.cpp)
target_include_directories(conversation_cache_test PRIVATE include)
add_test(NAME conversation_cache_test COMMAND conversation_cache_test)
add_executable(conversation_split_failure_test src/core/conversation_split_failure_test.cpp)
target_include_directories(conversation_split_failure_test PRIVATE include)
add_test(NAME conversation_split_failure_test COMMAND conversation_split_failure_test)
add_executable(conversation_memory_test src/core/conversation_memory_test.cpp src/core/conversation_memory.cpp)
target_include_directories(conversation_memory_test PRIVATE include)
add_test(NAME conversation_memory_test COMMAND conversation_memory_test)
add_executable(conversation_file_test src/core/conversation_file_test.cpp src/core/conversation_file.cpp)
target_include_directories(conversation_file_test PRIVATE include)
add_test(NAME conversation_file_test COMMAND conversation_file_test)
add_executable(session_save_reclaim_test src/core/session_save_reclaim_test.cpp src/core/conversation_file.cpp)
target_include_directories(session_save_reclaim_test PRIVATE include)
add_test(NAME session_save_reclaim_test COMMAND session_save_reclaim_test)
endif()
if(STRATA_HIP_GFX906)
if(NOT DEFINED CMAKE_HIP_ARCHITECTURES)
set(CMAKE_HIP_ARCHITECTURES gfx906 CACHE STRING "HIP architectures" FORCE)
endif()
enable_language(HIP)
find_package(hip REQUIRED)
find_package(hipblas REQUIRED)
add_library(CUDA::cudart INTERFACE IMPORTED)
target_link_libraries(CUDA::cudart INTERFACE hip::host)
target_compile_definitions(CUDA::cudart INTERFACE STRATA_HIP_GFX906=1 __HIP_PLATFORM_AMD__=1)
target_include_directories(CUDA::cudart BEFORE INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/strata/platform/hip_compat)
add_library(CUDA::cublas INTERFACE IMPORTED)
target_link_libraries(CUDA::cublas INTERFACE roc::hipblas CUDA::cudart)
set(CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/include/strata/platform/hip_compat)
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/include/strata/platform/hip_compat)
add_compile_definitions(STRATA_HIP_GFX906=1)
message(STATUS "Strata: HIP (gfx906 compat) enabled, arch ${CMAKE_HIP_ARCHITECTURES}")
set(_strata_gpu_runtime_target CUDA::cudart)
set(_strata_gpu_blas_target CUDA::cublas)
set(_strata_gpu_include_directories ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
elseif(STRATA_ENABLE_CUDA)
# #585: CMake links CUDA code with the STATIC runtime by default, and the targets below also link CUDA::cudart (the
# DLL's import library). MSVC with a CUDA 12.x toolkit - the experimental Pascal/Volta build - then fails with
# LNK1169 (every cudart symbol defined twice); GNU ld lets it pass. That build links the shared runtime alone. The
# ready-made engine (CUDA 13, no STRATA_EXPERIMENTAL_SM60) is unchanged; -DCMAKE_CUDA_RUNTIME_LIBRARY overrides.
if(MSVC AND STRATA_EXPERIMENTAL_SM60 AND NOT DEFINED CMAKE_CUDA_RUNTIME_LIBRARY)
set(CMAKE_CUDA_RUNTIME_LIBRARY Shared)
endif()
# #4023: the default below has to be set BEFORE enable_language(CUDA): since CMake 3.18 that call fills an unset
# CMAKE_CUDA_ARCHITECTURES with nvcc's own default (sm_75 for CUDA 13), so a test after it never saw "undefined" and
# a build with no option was an sm_75 build. -DCMAKE_CUDA_ARCHITECTURES and the CUDAARCHS environment variable win.
set(_strata_cuda_arch_defaulted OFF)
if(NOT DEFINED CMAKE_CUDA_ARCHITECTURES AND NOT DEFINED ENV{CUDAARCHS})
if(STRATA_BUILD_DSV41)
# #3935: the DeepSeek engine's own release shape (sm_86, sm_89, sm_120 + PTX 120, as packaging/build_dsv41_cuda.sh), so a hand
# build of strata-dsv41 with no architecture runs on an RTX 30, 40 or 50 card; the Qwen engine's default below is unchanged
set(CMAKE_CUDA_ARCHITECTURES "86-real;89-real;120" CACHE STRING "CUDA architectures")
else()
set(CMAKE_CUDA_ARCHITECTURES 120 CACHE STRING "CUDA architectures")
endif()
set(_strata_cuda_arch_defaulted ON)
endif()
enable_language(CUDA)
# `enable_language(CUDA)` gives the .cu compile rules but NOT the imported targets, so `CUDA::cudart` has to
# be brought in explicitly for the host-side tests that call cudaMalloc themselves.
find_package(CUDAToolkit REQUIRED)
option(STRATA_EXPERIMENTAL_SM60 "community build for GPUs below compute capability 7.5: Pascal sm_60, Volta sm_70 (#236)" OFF)
if(_strata_cuda_arch_defaulted)
# a recommendation, not a refusal: the release engine is built for 75;86;89;120, a hand build names its card
message(WARNING "Strata: CMAKE_CUDA_ARCHITECTURES was not set, so this build is for ${CMAKE_CUDA_ARCHITECTURES} only (sm_120 = RTX 50). For another "
"card pass it: -DCMAKE_CUDA_ARCHITECTURES=native (the GPUs in this PC), 75 (RTX 20), 86 (RTX 30), "
"89 (RTX 40), or a list like \"86;120\".")
endif()
foreach(_a IN LISTS CMAKE_CUDA_ARCHITECTURES)
string(REGEX REPLACE "-.*$" "" _base "${_a}") # 120-real / 120-virtual are still 120
# Turing port: developed and measured on sm_120 (RTX 50); the tf32 mma in the QSA scorer has a
# portable fp32-FMA fallback, so RTX 20 (75), 30 (86), RTX 40 (89) and RTX 50 (120) build too.
# Pre-sm_75 is refused.
if(_base STREQUAL "native" OR _base STREQUAL "all" OR _base STREQUAL "all-major")
continue()
endif()
# STRATA_EXPERIMENTAL_SM60: a Pascal (sm_60, GP100-class) or Volta (sm_70, V100 / Titan V, #236) build.
# These sit below every architecture the kernels were written for, so two intrinsics are replaced below
# their arch - `__dp4a` (6.1+) and `__nanosleep` (7.0+) - in include/strata/kernels/dp4a.hpp, which is
# bit-exact for the first and merely a shorter pause for the second (sm_70 has both). Community-tested,
# not in the ready-made engine; RTX 20/30/40/50 cards must use the release engine instead. It needs a CUDA
# 12.x toolkit: CUDA 13 dropped sm_60 and sm_70 (nvcc fails CMake's compiler test with "compute_70").
if(_base LESS 75 AND NOT STRATA_EXPERIMENTAL_SM60)
message(FATAL_ERROR
"Strata needs an NVIDIA GPU of compute capability 7.5 or newer (RTX 20 / 30 / 40 / 50 series), but "
"CMAKE_CUDA_ARCHITECTURES is '${CMAKE_CUDA_ARCHITECTURES}'. (A Pascal sm_60 or Volta sm_70 build is "
"-DSTRATA_EXPERIMENTAL_SM60=ON; upstream does not support it.)")
endif()
if(_base LESS 52)
message(FATAL_ERROR "CMAKE_CUDA_ARCHITECTURES '${CMAKE_CUDA_ARCHITECTURES}': even the experimental build "
"needs compute capability 5.2 (sm_52, Maxwell) or newer")
endif()
# Maxwell (sm_52, #3218): one reporter built and ran this on two Quadro M6000 (CUDA 12.x, Qwen3.8-Flash-Next
# IQ1, about 22 t/s). It has no native half-precision arithmetic, so it is slower per byte than Pascal; only
# this opt-in build gets the floor. Warn, do not refuse.
if(_base LESS 60)
message(WARNING "Building for sm_${_base} (Maxwell): community-tested on one reporter's Quadro M6000 only, "
"no native half-precision arithmetic, CUDA 12.x toolkit required (docs/OLDER_GPUS.md).")
endif()
# #220 / #224: RTX 50 engines built with CUDA 12.8 crashed in the prompt path on Linux (13.0 and newer did not)
if(_base GREATER_EQUAL 120 AND CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 13.0)
message(WARNING "Building for sm_${_base} (RTX 50) with CUDA ${CMAKE_CUDA_COMPILER_VERSION}: engines built with "
"CUDA 12.8 crashed on long prompts on these cards (issues #220, #224). Use CUDA 13.0 or newer.")
endif()
endforeach()
if(STRATA_EXPERIMENTAL_SM60)
# every target sees it: the Pascal / Volta code paths (the Volta prompt attention, PR #600) are compiled into this
# build only, so the ready-made engine (sm_75+) keeps exactly the code it had
add_compile_definitions(STRATA_EXPERIMENTAL_SM60=1)
endif()
message(STATUS "Strata: CUDA enabled, arch ${CMAKE_CUDA_ARCHITECTURES}")
set(_strata_gpu_runtime_target CUDA::cudart)
set(_strata_gpu_blas_target CUDA::cublas)
set(_strata_gpu_include_directories ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
elseif(STRATA_ENABLE_HIP)
set(_strata_gpu_runtime_target strata_hip_runtime)
set(_strata_gpu_blas_target ${STRATA_HIP_BLAS_TARGETS})
set(_strata_gpu_include_directories ${STRATA_HIP_COMPAT_INCLUDE_DIR})
endif()
# Warnings live on an INTERFACE target the project's own code links, NOT in add_compile_options: applied
# globally they would also hit FetchContent'd Catch2, whose -W4 warnings are not this project's to fix and
# whose /WX failures would be indistinguishable from our own.
#
# Every flag is guarded by COMPILE_LANGUAGE:CXX. Without that guard nvcc receives bare `/W4 /utf-8` and dies
# with "A single input file is required for a non-link phase" - it parses them as inputs rather than host
# flags. Host flags for CUDA must go through -Xcompiler, which is what COMPILE_LANGUAGE:CUDA does below.
add_library(strata_warnings INTERFACE)
target_compile_options(strata_warnings INTERFACE
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/W4>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/utf-8>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:-Wall>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:-Wextra>
$<$<AND:$<COMPILE_LANGUAGE:CUDA>,$<CXX_COMPILER_ID:MSVC>>:-Xcompiler=/W4>
$<$<COMPILE_LANGUAGE:HIP>:-Wall>
$<$<COMPILE_LANGUAGE:HIP>:-Wextra>)
if(STRATA_WERROR)
target_compile_options(strata_warnings INTERFACE
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/WX>
$<$<AND:$<COMPILE_LANGUAGE:CXX>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:-Werror>
$<$<COMPILE_LANGUAGE:HIP>:-Werror>)
endif()
# ---- strata_artifact -------------------------------------------------------
# Header-only, and that is a deliberate property rather than an unfinished split: the GGUF reader and the
# scalar dequantizers hold no state worth hiding, and a header-only boundary cannot introduce a
# duplicate-symbol or missing-declaration bug into code that is already validated against the real artifact.
add_library(strata_artifact INTERFACE)
target_include_directories(strata_artifact INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(strata_artifact INTERFACE strata_warnings)
add_executable(strata-gguf src/artifact/gguf_reader.cpp)
target_link_libraries(strata-gguf PRIVATE strata_artifact)
add_executable(strata-dequant src/artifact/dequant.cpp)
target_link_libraries(strata-dequant PRIVATE strata_artifact)
# this file is now nothing but the CLI entry point, and its inherited guard spells that as a "selftest"
target_compile_definitions(strata-dequant PRIVATE STRATA_DEQUANT_SELFTEST)
if(STRATA_BUILD_TESTS)
add_executable(batch_telemetry_test src/core/batch_telemetry_test.cpp)
target_include_directories(batch_telemetry_test PRIVATE include)
add_test(NAME batch_telemetry_test COMMAND batch_telemetry_test)
add_executable(logit_bias_test src/core/logit_bias_test.cpp)
target_include_directories(logit_bias_test PRIVATE include)
add_test(NAME logit_bias_test COMMAND logit_bias_test)
# the reader refuses a duplicate tensor name at open, naming it (a synthetic GGUF: no model, no GPU)
add_executable(gguf_reader_test src/artifact/gguf_reader_test.cpp)
target_link_libraries(gguf_reader_test PRIVATE strata_artifact)
add_test(NAME gguf_reader_test COMMAND gguf_reader_test)
# a split model's shards and their joint view: missing shard, split keys, duplicates, truncation (synthetic)
add_executable(gguf_split_test tests/core/gguf_split_test.cpp)
target_link_libraries(gguf_split_test PRIVATE strata_artifact)
add_test(NAME gguf_split_test COMMAND gguf_split_test)
# the model families (qwen4exp, qwen35moe): the guards, the geometry from metadata, the layout tables (synthetic)
add_executable(model_family_test tests/core/model_family_test.cpp)
target_link_libraries(model_family_test PRIVATE strata_artifact)
add_test(NAME model_family_test COMMAND model_family_test)
# how --batch cuts a prompt read into pieces (a layer split with no slot decoding must still reach a BYIELD)
add_executable(batch_read_test tests/program/batch_read_test.cpp)
target_include_directories(batch_read_test PRIVATE include)
add_test(NAME batch_read_test COMMAND batch_read_test)
# the model's geometry from its metadata, and the check against the kernels' compiled shape (synthetic)
add_executable(gguf_geometry_test tests/core/gguf_geometry_test.cpp)
target_link_libraries(gguf_geometry_test PRIVATE strata_artifact)
add_test(NAME gguf_geometry_test COMMAND gguf_geometry_test)
# the hipBLASLt tuning header's architecture gate: RDNA4 siblings share solution IDs, RDNA3 siblings share
# theirs, cross-family pairs and version mismatches stay rejected (synthetic tables: no model, no GPU)
add_executable(tuning_header_arch_family_test tests/prefill/tuning_header_arch_family_test.cpp)
target_include_directories(tuning_header_arch_family_test PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/src/prefill")
add_test(NAME tuning_header_arch_family_test COMMAND tuning_header_arch_family_test)
# the auto layer split's minimum stage size (#1616): header-only rule, no GPU
add_executable(split_rules_test tests/program/split_rules_test.cpp)
target_include_directories(split_rules_test PRIVATE include)
add_test(NAME split_rules_test COMMAND split_rules_test)
# #3797: the out-of-memory report at start (per card free/total, the largest other programs): the text is a pure
# function; the driver query (NVML, loaded at run time) is only checked to be safe to call. No GPU needed.
add_executable(gpu_report_test tests/program/gpu_report_test.cpp)
target_include_directories(gpu_report_test PRIVATE include)
target_link_libraries(gpu_report_test PRIVATE ${CMAKE_DL_LIBS})
add_test(NAME gpu_report_test COMMAND gpu_report_test)
# #3702: the later-stage reserve of the layer split search (header-only arithmetic, pinned against the old expression)
add_executable(stage_reserve_test tests/program/stage_reserve_test.cpp)
target_include_directories(stage_reserve_test PRIVATE include)
add_test(NAME stage_reserve_test COMMAND stage_reserve_test)
# the prompt planner's host-memory model (#2824): header-only, no GPU
add_executable(prefill_plan_test tests/program/prefill_plan_test.cpp)
target_include_directories(prefill_plan_test PRIVATE include)
add_test(NAME prefill_plan_test COMMAND prefill_plan_test)
# the default CPU prefill share is not armed when every expert is resident (#1595): header-only rule, no GPU
add_executable(share_rules_test tests/prefill/share_rules_test.cpp)
target_include_directories(share_rules_test PRIVATE include)
add_test(NAME share_rules_test COMMAND share_rules_test)
# the Windows HIP auto prefill chunk cap (#1630) and the commit-limit verdict (#1607): header-only, no GPU
add_executable(host_limits_test tests/platform/host_limits_test.cpp)
target_include_directories(host_limits_test PRIVATE include)
add_test(NAME host_limits_test COMMAND host_limits_test)
endif()
# ---- strata_plan ----------------------------------------------------------
# Also header-only. A "planner" that hides state would be one whose decision is not inspectable, and the
# whole point of P1.S9 is that the decision is arithmetic you can check by hand.
add_library(strata_plan INTERFACE)
target_include_directories(strata_plan INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(strata_plan INTERFACE strata_warnings)
add_executable(strata-plan src/plan/plan_main.cpp)
target_link_libraries(strata-plan PRIVATE strata_plan)
# ---- strata_core (GPU runtime) ---------------------------------------------
# Only built when a GPU backend is on, so the Phase 1 targets keep working without a toolkit.
if(STRATA_ENABLE_CUDA OR STRATA_ENABLE_HIP)
add_library(strata_core STATIC src/core/device.cu src/core/pinned.cu src/platform/memory.cpp src/core/graph.cpp src/core/weights.cpp
src/core/layout.cpp)
target_include_directories(strata_core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
# the DFlash2 drafter (src/core/dflash2.cpp, src/kernels/cuda/dflash2.cu) exists in the CUDA and HIP builds; the Metal and SYCL builds compile without it
target_compile_definitions(strata_core PUBLIC STRATA_HAVE_DFLASH2=1)
if(STRATA_ENABLE_CUDA AND STRATA_EXPERIMENTAL_SM60)
# the runtime device check (src/core/device.cu) admits the cards this build was made for (#236)
target_compile_definitions(strata_core PRIVATE STRATA_EXPERIMENTAL_SM60=1)
endif()
# The selected runtime is PUBLIC because graph.cpp is a HOST translation unit that calls cudaGraphLaunch and
# cudaEventQuery: the .cu sources get cudart through the CUDA language rules, a .cpp does not, and the
# linker error ("unresolved external symbol cudaGraphInstantiate") names the function rather than the cause.
target_link_libraries(strata_core PUBLIC strata_plan strata_warnings ${_strata_gpu_runtime_target})
add_executable(strata-device src/core/device_main.cpp)
target_link_libraries(strata-device PRIVATE strata_core)
add_executable(strata-load src/core/load_main.cpp)
target_link_libraries(strata-load PRIVATE strata_core strata_kernels)
add_executable(strata-concurrent src/core/concurrent_main.cpp)
target_include_directories(strata-concurrent PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(strata-concurrent PRIVATE strata_core strata_kernels ${_strata_gpu_runtime_target})
add_executable(strata-overlap src/core/overlap_main.cpp)
# a .cpp that calls cudaMemcpyAsync itself needs the toolkit headers and cudart explicitly
target_include_directories(strata-overlap PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(strata-overlap PRIVATE strata_core strata_kernels ${_strata_gpu_runtime_target})
# The load test needs the 34 GB artifact, so it is registered ONLY where that artifact exists. A test that
# silently skips is a test that stops running; an absent registration is visible in the configure output.
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/pack/full/experts.bin)
add_test(NAME expert_arena_load
COMMAND strata-load --file ${CMAKE_CURRENT_SOURCE_DIR}/pack/full/experts.bin --layers 1)
set_tests_properties(expert_arena_load PROPERTIES PASS_REGULAR_EXPRESSION "per-layer checksums")
else()
message(STATUS "strata: pack/full/experts.bin absent - the expert arena load test is NOT registered")
endif()
# ---- strata_kernels (P2.S2) ----
# Naive first, each with a parity test against a reference that was itself validated against ggml. The
# kernels link `strata_artifact` for the scalar dequantizers, because using the VALIDATED scalar path as the
# reference is the whole point - a kernel checked against another copy of itself proves nothing.
add_library(strata_kernels STATIC src/kernels/cuda/dequant_s2.cu src/kernels/cuda/s2_gemv.cu
src/kernels/cuda/s_gemv.cu
src/kernels/cuda/s_gemv_pair.cu
src/kernels/cuda/s2_gemv_quads.cu
src/kernels/cuda/s2_gemv_fast.cu
src/kernels/cuda/router_top10.cu
src/kernels/cuda/quantize_act.cu
src/kernels/cuda/rope.cu
src/kernels/cuda/sampler.cu
src/kernels/cuda/s2_gemv_q8.cu
src/kernels/cuda/s2_expert_grouped.cu
src/kernels/cuda/shared_expert.cu
src/kernels/cuda/gr.cu
src/kernels/cuda/gdn.cu
src/kernels/cuda/qsa.cu
src/kernels/cuda/elementwise.cu
src/kernels/cuda/bf16_gemv.cu
src/kernels/cuda/native_mmvq.cu
src/kernels/cuda/native_bf16.cu
src/kernels/cuda/native_gr_norm.cu
src/kernels/cuda/native_moe.cu
src/kernels/cuda/native_gdn.cu
src/kernels/cuda/native_gr_postops.cu
src/kernels/cuda/native_router.cu
src/kernels/cuda/route_prior.cu
src/kernels/cuda/native_gdn_preprocess.cu
src/kernels/cuda/native_qsa.cu
src/kernels/cuda/native_rope.cu
src/kernels/cuda/native_ple_postops.cu
src/kernels/cuda/native_flash_attn.cu
src/kernels/cuda/native_qsa_indexer.cu
src/kernels/cuda/native_qsa_score.cu
src/kernels/cuda/ple.cu
src/kernels/cuda/kv_q8.cu
src/kernels/cuda/kv_stream.cu
src/kernels/cuda/kv_q4.cu
src/kernels/cuda/fused_gr.cu
src/kernels/cuda/cvec.cu
src/kernels/cuda/qsa_decode_attn.cu
src/kernels/cuda/qsa_prompt_attn.cu
src/kernels/cuda/fused_gdn.cu
src/kernels/cuda/dequant_bf16.cu
src/kernels/cuda/qsa_select.cu
src/kernels/cuda/qsa_pattn.cu
src/kernels/cuda/verify_kernels.cu
src/kernels/cuda/dflash2.cu
src/kernels/cuda/iq_kernels.cu
src/kernels/ngram.cpp
src/ngram/ple_reader.cpp
src/platform/direct_file.cpp)
target_link_libraries(strata_kernels PUBLIC strata_core strata_artifact)
# Exact gfx1100 prompt scorer (PR1825 method port): optional standalone test,
# also available where the published source omits tests/.
option(STRATA_PARITY_GFX1100_QSA_SCORES "Build the exact gfx1100 QSA score/ID check" OFF)
if(STRATA_ENABLE_HIP AND (STRATA_BUILD_TESTS OR STRATA_PARITY_GFX1100_QSA_SCORES))
set_source_files_properties(src/kernels/cuda/qsa_scores_gfx1100_test.cu
src/kernels/qsa_scores_gfx1100_parity.cpp PROPERTIES LANGUAGE HIP)
add_executable(qsa_scores_gfx1100_parity src/kernels/qsa_scores_gfx1100_parity.cpp
src/kernels/cuda/qsa_scores_gfx1100_test.cu)
target_include_directories(qsa_scores_gfx1100_parity PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(qsa_scores_gfx1100_parity PRIVATE strata_kernels ${_strata_gpu_runtime_target})
enable_testing()
add_test(NAME qsa_scores_gfx1100_parity COMMAND qsa_scores_gfx1100_parity --full)
set_tests_properties(qsa_scores_gfx1100_parity PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 180
ENVIRONMENT "STRATA_SCORES_THREAD=1;STRATA_SCORES_THREAD_TRACE=1")
endif()
# perf-review D-1: the tensor-core prompt attention vs the FP32 kernel and FP64 (GPU, synthetic, no model); with
# the tests, or alone with -DSTRATA_PARITY_PROMPT_ATTN=ON. CUDA only: HIP compiles the tensor-core kernel out.
if((STRATA_BUILD_TESTS OR STRATA_PARITY_PROMPT_ATTN) AND STRATA_ENABLE_CUDA)
add_executable(qsa_prompt_attn_parity src/kernels/qsa_prompt_attn_parity.cpp)
target_include_directories(qsa_prompt_attn_parity PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
target_link_libraries(qsa_prompt_attn_parity PRIVATE strata_kernels CUDA::cudart)
endif()
# PR #3943: STRATA_PATTN_SM60's score and value kernels against the decode kernels' output (the Daily's chunk kernel and
# the merge), bitwise, on random int8 K/V (GPU, synthetic, no model). Only in the experimental sm_60 build; it skips
# (77) on a build without the kernels.
if(STRATA_BUILD_TESTS AND STRATA_ENABLE_CUDA AND STRATA_EXPERIMENTAL_SM60)
add_executable(qsa_pattn_parity src/kernels/qsa_pattn_parity.cpp)
target_include_directories(qsa_pattn_parity PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
target_link_libraries(qsa_pattn_parity PRIVATE strata_kernels CUDA::cudart)
add_test(NAME qsa_pattn_parity COMMAND qsa_pattn_parity)
set_tests_properties(qsa_pattn_parity PROPERTIES SKIP_RETURN_CODE 77)
endif()
# PR #3820: STRATA_SELECT_LONG's multi-CTA top-k and shuffle-free decode scorer against qsa_block_topk and
# qsa_block_scores, bitwise, on random scores (GPU, synthetic, no model). The switches force the new kernels on any
# card; the "n3" case fixes three CTAs a query. Only in the experimental sm_60 build (skips with 77 otherwise).
if(STRATA_BUILD_TESTS AND STRATA_ENABLE_CUDA AND STRATA_EXPERIMENTAL_SM60)
add_executable(qsa_select_long_parity src/kernels/qsa_select_long_parity.cpp)
target_include_directories(qsa_select_long_parity PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
target_link_libraries(qsa_select_long_parity PRIVATE strata_kernels CUDA::cudart)
add_test(NAME qsa_select_long_parity COMMAND qsa_select_long_parity)
add_test(NAME qsa_select_long_parity_n3 COMMAND qsa_select_long_parity)
set_tests_properties(qsa_select_long_parity qsa_select_long_parity_n3 PROPERTIES SKIP_RETURN_CODE 77)
set_tests_properties(qsa_select_long_parity PROPERTIES ENVIRONMENT "STRATA_SCORES_DX=1;STRATA_TOPK_MULTI=1")
set_tests_properties(qsa_select_long_parity_n3 PROPERTIES ENVIRONMENT "STRATA_SCORES_DX=1;STRATA_TOPK_MULTI=1;STRATA_TOPK_MULTI_N=3")
endif()
# PR #600: the Volta (sm_70) prompt attention's latency pipeline against the FP32 kernel it replaces. Only in the
# experimental sm_60/sm_70 build, where prompt_attn_v70_kernel is compiled in.
if(STRATA_BUILD_TESTS AND STRATA_ENABLE_CUDA AND STRATA_EXPERIMENTAL_SM60)
add_executable(qsa_prompt_attn_sm70_bench bench/qsa_prompt_attn_sm70_bench.cpp)
target_include_directories(qsa_prompt_attn_sm70_bench PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
target_link_libraries(qsa_prompt_attn_sm70_bench PRIVATE strata_kernels CUDA::cudart)
endif()
# #512: the top-k's active-block bound (Turing's dispatch, forced on any card by STRATA_TOPK_ACTIVE_ANY) against
# the capacity rule, selected ids bitwise
if(STRATA_BUILD_TESTS AND STRATA_ENABLE_CUDA)
add_executable(qsa_topk_active_parity src/kernels/qsa_topk_active_parity.cpp)
target_include_directories(qsa_topk_active_parity PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
target_link_libraries(qsa_topk_active_parity PRIVATE strata_kernels CUDA::cudart)
add_test(NAME qsa_topk_active_parity COMMAND qsa_topk_active_parity)
set_tests_properties(qsa_topk_active_parity PROPERTIES SKIP_RETURN_CODE 77)
# STRATA_QSA_SLOTS: a batch window's scores, top-k and attention as one launch over rows of different slots
# against one call per row, bitwise (synthetic, no model)
add_executable(qsa_slots_parity src/kernels/qsa_slots_parity.cpp)
target_include_directories(qsa_slots_parity PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
target_link_libraries(qsa_slots_parity PRIVATE strata_kernels CUDA::cudart)
add_test(NAME qsa_slots_parity COMMAND qsa_slots_parity)
add_test(NAME qsa_slots_parity_lanecell COMMAND qsa_slots_parity)
set_tests_properties(qsa_slots_parity qsa_slots_parity_lanecell PROPERTIES SKIP_RETURN_CODE 77)
set_tests_properties(qsa_slots_parity_lanecell PROPERTIES ENVIRONMENT "STRATA_ATTN_LANECELL=1")
# fork: the Daily's decode attention (STRATA_ATTN_DAILY=1: PR #540's chunk kernel on every card and the bounded
# merge) against upstream's kernels, bitwise; the production entries checked with the variable off and on
add_executable(qsa_merge_daily_parity src/kernels/qsa_merge_daily_parity.cpp)
target_include_directories(qsa_merge_daily_parity PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
target_link_libraries(qsa_merge_daily_parity PRIVATE strata_kernels CUDA::cudart)
add_test(NAME qsa_merge_daily_parity_default COMMAND qsa_merge_daily_parity)
add_test(NAME qsa_merge_daily_parity_daily COMMAND qsa_merge_daily_parity)
set_tests_properties(qsa_merge_daily_parity_default PROPERTIES ENVIRONMENT "STRATA_ATTN_DAILY=0"
SKIP_RETURN_CODE 77)
set_tests_properties(qsa_merge_daily_parity_daily PROPERTIES ENVIRONMENT "STRATA_ATTN_DAILY=1"
SKIP_RETURN_CODE 77)
# the prompt path's block scores on the thread-per-head kernel (Turing) against the warp kernel, bitwise
add_executable(qsa_scores_parity src/kernels/qsa_scores_parity.cpp)
target_include_directories(qsa_scores_parity PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
target_link_libraries(qsa_scores_parity PRIVATE strata_kernels CUDA::cudart)
add_test(NAME qsa_scores_parity COMMAND qsa_scores_parity)
set_tests_properties(qsa_scores_parity PROPERTIES SKIP_RETURN_CODE 77 ENVIRONMENT "STRATA_SCORES_THREAD=1")
endif()
# QSAROWS (docs/QSAROWS.md): what the T rows of a verification window cost in a QSA layer's decode kernels at a long
# context (block scores, top-k, attention over each row's selection, indexer append), row by row; GPU, synthetic, no
# model. Not in the default build: `cmake --build <dir> --target qsa_rows_bench`.
if(STRATA_ENABLE_CUDA)
add_executable(qsa_rows_bench EXCLUDE_FROM_ALL src/kernels/qsa_rows_bench.cpp)
target_include_directories(qsa_rows_bench PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
target_link_libraries(qsa_rows_bench PRIVATE strata_kernels CUDA::cudart)
endif()
# the QSA selection (block scores on the warp kernel and the tensor-core one, top-k): a GPU bench with a
# scorer-vs-scorer comparison and an FP64 accuracy gate (synthetic, no model); the ctest runs a 32K context under a
# 262144-cell capacity (the top-k dispatch follows the capacity) and exits non-zero on a failed gate
if((STRATA_BUILD_TESTS OR STRATA_PARITY_PROMPT_ATTN) AND STRATA_ENABLE_HIP)
add_executable(qsa_select_bench src/kernels/qsa_select_bench.cpp)
set_source_files_properties(src/kernels/qsa_select_bench.cpp PROPERTIES LANGUAGE HIP)
target_link_libraries(qsa_select_bench PRIVATE strata_kernels strata_hip_runtime)
add_test(NAME qsa_select_bench COMMAND qsa_select_bench 32768 256 3 262144)
# the FP32 tiled scorer (STRATA_SELECT_SIMT=1) on HIP: FP64 gate and selections against the warp kernel at 131K
add_test(NAME qsa_select_bench_fp32_tiled COMMAND qsa_select_bench 131072 256 3 262144)
set_tests_properties(qsa_select_bench_fp32_tiled PROPERTIES ENVIRONMENT "STRATA_SELECT_SIMT=1")
endif()
# the same on CUDA: the tensor-core scorer (sm_80+) or the FP32 tiled one (below sm_80, and forced on any card by
# STRATA_QSA_WARP=1 in the second test) against the warp kernel and FP64, at 32K and 131K
if(STRATA_BUILD_TESTS AND STRATA_ENABLE_CUDA)
add_executable(qsa_select_bench src/kernels/qsa_select_bench.cpp)
target_include_directories(qsa_select_bench PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
target_link_libraries(qsa_select_bench PRIVATE strata_kernels CUDA::cudart)
add_test(NAME qsa_select_bench COMMAND qsa_select_bench 32768 256 3 262144)
add_test(NAME qsa_select_bench_fp32_tiled COMMAND qsa_select_bench 131072 256 3 262144)
set_tests_properties(qsa_select_bench_fp32_tiled PROPERTIES ENVIRONMENT "STRATA_QSA_WARP=1;STRATA_SELECT_SIMT=1")
endif()
if(STRATA_BUILD_TESTS AND STRATA_ENABLE_CUDA) # the prompt attention on a recorded selection (STRATA_QSA_DUMP)
add_executable(pa_bench tests/cuda/pa_bench.cpp)
target_include_directories(pa_bench PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
target_link_libraries(pa_bench PRIVATE strata_kernels CUDA::cudart)
endif()
# plan v0.3 P6: llama.cpp's block layouts and codebook grids for the i-quant formats (MIT, third_party/ggml)
target_include_directories(strata_kernels PRIVATE third_party/ggml)
# Match the pinned llama.cpp CUDA arithmetic contract for this adapter only.
if(STRATA_ENABLE_CUDA)
set_source_files_properties(src/kernels/cuda/native_mmvq.cu src/kernels/cuda/native_bf16.cu
src/kernels/cuda/native_gr_norm.cu
src/kernels/cuda/native_moe.cu
src/kernels/cuda/native_gdn.cu
src/kernels/cuda/native_gr_postops.cu
src/kernels/cuda/native_router.cu
src/kernels/cuda/native_gdn_preprocess.cu
src/kernels/cuda/native_qsa.cu
src/kernels/cuda/native_rope.cu
src/kernels/cuda/native_ple_postops.cu
src/kernels/cuda/native_flash_attn.cu
src/kernels/cuda/native_qsa_indexer.cu
src/kernels/cuda/native_qsa_score.cu
PROPERTIES COMPILE_OPTIONS "$<IF:$<BOOL:${STRATA_HIP_GFX906}>,,--use_fast_math>")
elseif(STRATA_ENABLE_HIP)
# Keep Q8_K's multiply and magic-bias rounding as separate FP32 operations on HIP; contraction to an
# FMA changes tie results. The embedding gather also relies on separately rounded operations.
set_source_files_properties(src/kernels/cuda/elementwise.cu src/kernels/cuda/quantize_act.cu
PROPERTIES COMPILE_OPTIONS "-ffp-contract=off")
endif()
set(STRATA_ORACLE_SOURCE_DIR "" CACHE PATH "Pinned llama.cpp source for independent CUDA parity tests")
set(STRATA_ORACLE_BUILD_DIR "" CACHE PATH "Matching existing shared llama.cpp build")
# This oracle is a CUDA ggml build; do not pull CUDA libraries into a HIP build.
if(STRATA_ENABLE_CUDA AND STRATA_ORACLE_SOURCE_DIR AND STRATA_ORACLE_BUILD_DIR)
foreach(parity IN ITEMS native_mmvq native_bf16 native_shared native_gr_norm native_moe native_gdn native_gr_postops native_router native_gdn_preprocess native_gdn_pipeline native_qsa native_rope native_ple_postops native_flash_attn native_qsa_indexer native_qsa_score)
add_executable(${parity}_parity bench/micro/${parity}_parity.cpp)
target_include_directories(${parity}_parity PRIVATE
"${STRATA_ORACLE_SOURCE_DIR}/ggml/include" ${_strata_gpu_include_directories})
target_compile_definitions(${parity}_parity PRIVATE GGML_SHARED GGML_BACKEND_SHARED)
target_link_libraries(${parity}_parity PRIVATE strata_kernels ${_strata_gpu_runtime_target})
foreach(component IN ITEMS ggml ggml-base ggml-cpu ggml-cuda)
string(REPLACE "-" "_" variable "STRATA_ORACLE_${component}")
find_library(${variable} NAMES ${component} PATHS
"${STRATA_ORACLE_BUILD_DIR}/ggml/src" "${STRATA_ORACLE_BUILD_DIR}/ggml/src/${component}"
"${STRATA_ORACLE_BUILD_DIR}/bin" NO_DEFAULT_PATH REQUIRED)
target_link_libraries(${parity}_parity PRIVATE "${${variable}}")
endforeach()
add_test(NAME ${parity}_parity COMMAND ${parity}_parity --selftest)
if(WIN32)
set_tests_properties(${parity}_parity PROPERTIES
ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:${STRATA_ORACLE_BUILD_DIR}/bin")
endif()
endforeach()
endif()
# ---- strata_engine (P2.S5) -----------------------------------------------------
# The COMPOSED layer lives here and not in `strata_core`, because it calls kernels and `strata_kernels`
# already links `strata_core` - putting `gdn_layer` in core would make the two mutually dependent. A third
# library is the honest shape of the dependency graph: core knows about devices and weights, kernels know
# about arithmetic, and this one knows the ORDER the arithmetic happens in.
add_library(strata_engine STATIC src/core/layer.cpp src/core/session.cpp src/core/expert_source.cpp src/core/remote_experts.cpp
src/core/remote_expert_opt.cu
src/core/expert_cache.cpp src/core/native_head.cpp src/core/native_dense.cpp src/core/verify.cpp src/core/mtp.cpp src/core/dflash2.cpp
src/core/conversation_snapshot.cpp src/core/conversation_state.cpp src/core/conversation_memory.cpp src/core/peer_experts.cpp
src/core/arch_defaults.cpp src/core/nv_defaults.cpp src/core/vmm.cpp src/core/conversation_file.cpp)
target_include_directories(strata_engine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(strata_engine PUBLIC strata_core strata_kernels strata_kernels_cpu ${_strata_gpu_runtime_target})
if(STRATA_BUILD_TESTS AND STRATA_ENABLE_CUDA AND NOT STRATA_HIP_GFX906)
add_executable(host_graph_launch_test tests/core/host_graph_launch_test.cu)
target_include_directories(host_graph_launch_test PRIVATE include)
target_link_libraries(host_graph_launch_test PRIVATE CUDA::cudart Threads::Threads)
add_test(NAME host_graph_launch_test COMMAND host_graph_launch_test)
set_tests_properties(host_graph_launch_test PROPERTIES TIMEOUT 30)
endif()
if(STRATA_BUILD_TESTS AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/core/exchange_storage_test.cpp")
add_executable(exchange_storage_test tests/core/exchange_storage_test.cpp)
target_include_directories(exchange_storage_test PRIVATE include)
target_link_libraries(exchange_storage_test PRIVATE Threads::Threads)
add_test(NAME exchange_storage_test COMMAND exchange_storage_test)
set_tests_properties(exchange_storage_test PROPERTIES TIMEOUT 60)
endif()
# available_host_bytes(): MemAvailable, cgroup limits and the operator's --memory-limit-mib cap on fixture directories
# - no GPU, no CUDA
if(STRATA_BUILD_TESTS AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/core/memory_guard_test.cpp")
add_executable(memory_guard_test tests/core/memory_guard_test.cpp src/core/conversation_memory.cpp)
target_include_directories(memory_guard_test PRIVATE include)
add_test(NAME memory_guard_test COMMAND memory_guard_test)
set_tests_properties(memory_guard_test PROPERTIES TIMEOUT 60)
endif()
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/core/file_expert_source_test.cpp")
enable_testing()
add_executable(file_expert_source_test tests/core/file_expert_source_test.cpp)
target_link_libraries(file_expert_source_test PRIVATE strata_engine)
add_test(NAME file_expert_source_test COMMAND file_expert_source_test)
set_tests_properties(file_expert_source_test PROPERTIES TIMEOUT 60)
add_executable(vmm_test tests/core/vmm_test.cpp)
target_link_libraries(vmm_test PRIVATE strata_engine)
add_test(NAME vmm_test COMMAND vmm_test)
set_tests_properties(vmm_test PROPERTIES TIMEOUT 60)
# The resident RAM mode with a layer split: the copy keeps each stage's lend region, and the experts the
# prompt path borrows are served from RAM (the file source reads none of them). The device calls are
# wrapped - conversation_transfer_test's pattern - and `pin = false` keeps the copy pageable, so this
# runs on a CPU-only PC.
if(NOT STRATA_ENABLE_HIP AND UNIX AND NOT APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
add_executable(split_lend_complement_test tests/core/split_lend_complement_test.cpp)
target_link_libraries(split_lend_complement_test PRIVATE strata_engine)
target_link_options(split_lend_complement_test PRIVATE
-Wl,--wrap=cudaMalloc -Wl,--wrap=cudaFree -Wl,--wrap=cudaMemset -Wl,--wrap=cudaMemcpy
-Wl,--wrap=cudaMemcpyAsync -Wl,--wrap=cudaStreamSynchronize -Wl,--wrap=cudaDeviceSynchronize
-Wl,--wrap=cudaGetLastError -Wl,--wrap=cudaGetDevice -Wl,--wrap=cudaSetDevice
-Wl,--wrap=cudaMemGetInfo -Wl,--wrap=cudaHostAlloc -Wl,--wrap=cudaFreeHost)
add_test(NAME split_lend_complement_test COMMAND split_lend_complement_test)
set_tests_properties(split_lend_complement_test PROPERTIES ENVIRONMENT "CUDA_VISIBLE_DEVICES=-1" TIMEOUT 120)
endif()
endif()
# #477: the learned expert profile (--expert-profile-save): its order and make_profile.py's file format (CPU only)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/core/expert_profile_save_test.cpp")
enable_testing()
add_executable(expert_profile_save_test tests/core/expert_profile_save_test.cpp)
target_link_libraries(expert_profile_save_test PRIVATE strata_engine)
add_test(NAME expert_profile_save_test COMMAND expert_profile_save_test)
set_tests_properties(expert_profile_save_test PROPERTIES TIMEOUT 60)
endif()
# the profile header's version is enforced: a version other than 1 is refused by its number (CPU only)
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/core/expert_profile_version_test.cpp")
enable_testing()
add_executable(expert_profile_version_test tests/core/expert_profile_version_test.cpp)
target_link_libraries(expert_profile_version_test PRIVATE strata_engine)
add_test(NAME expert_profile_version_test COMMAND expert_profile_version_test)
set_tests_properties(expert_profile_version_test PROPERTIES TIMEOUT 60)
endif()
if(STRATA_BUILD_TESTS AND STRATA_NATIVE_EXPERTS)
# native_experts.txt v2-v4 (the real packs' copies and the per-role shard column) and the arena filled from
# synthetic GGUF shards at the real expert dimensions
add_executable(expert_layout_test tests/core/expert_layout_test.cpp)
target_link_libraries(expert_layout_test PRIVATE strata_engine)
add_test(NAME expert_layout_test COMMAND expert_layout_test ${CMAKE_CURRENT_SOURCE_DIR}/tests/data/native_experts)
set_tests_properties(expert_layout_test PROPERTIES TIMEOUT 60)
# #326: a native pack's BF16 (--compat-bf16) PLE key stays in the arena; a quantized key row loads as before
add_executable(native_dense_ple_key_test tests/core/native_dense_ple_key_test.cpp)
target_link_libraries(native_dense_ple_key_test PRIVATE strata_engine)
add_test(NAME native_dense_ple_key_test COMMAND native_dense_ple_key_test)
set_tests_properties(native_dense_ple_key_test PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 60)
# #369: per-layer admission on a sized (native pack) cache starts each layer in its own range
add_executable(expert_cache_per_layer_test tests/core/expert_cache_per_layer_test.cpp)
target_link_libraries(expert_cache_per_layer_test PRIVATE strata_engine)
add_test(NAME expert_cache_per_layer_test COMMAND expert_cache_per_layer_test)
set_tests_properties(expert_cache_per_layer_test PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 60)
add_executable(expert_cache_segmented_test tests/core/expert_cache_segmented_test.cpp)
target_link_libraries(expert_cache_segmented_test PRIVATE strata_engine)
add_test(NAME expert_cache_segmented_test COMMAND expert_cache_segmented_test)
set_tests_properties(expert_cache_segmented_test PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 60)
# STRATA_MMQ_DIRECT: slot_guard / slot_room / slot_tail on the uniform and the sized cache, and the default
# (no guard) sized layout unchanged
add_executable(expert_cache_slot_guard_test tests/core/expert_cache_slot_guard_test.cpp)
target_link_libraries(expert_cache_slot_guard_test PRIVATE strata_engine)
add_test(NAME expert_cache_slot_guard_test COMMAND expert_cache_slot_guard_test)
set_tests_properties(expert_cache_slot_guard_test PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 60)
endif()
if(STRATA_BUILD_CONVERSATION_TESTS)
add_executable(conversation_snapshot_test src/core/conversation_snapshot_test.cpp)
target_link_libraries(conversation_snapshot_test PRIVATE strata_engine)
add_test(NAME conversation_snapshot_test COMMAND conversation_snapshot_test)
set_tests_properties(conversation_snapshot_test PROPERTIES SKIP_RETURN_CODE 77 TIMEOUT 120)
# the same test with a GDN state over 1 MiB and the opt-in background checkpoint copy (STRATA_CKPT_ASYNC=1), alone and with
# recent states kept in VRAM (STRATA_CKPT_VRAM_SLOTS): every checkpoint, capture and restore must give the same bytes
add_test(NAME conversation_snapshot_test_ckpt_async COMMAND conversation_snapshot_test)
set_tests_properties(conversation_snapshot_test_ckpt_async PROPERTIES
ENVIRONMENT "STRATA_TEST_BIG_GDN=1;STRATA_CKPT_ASYNC=1" SKIP_RETURN_CODE 77 TIMEOUT 300)
add_test(NAME conversation_snapshot_test_ckpt_vram_slots COMMAND conversation_snapshot_test)
set_tests_properties(conversation_snapshot_test_ckpt_vram_slots PROPERTIES
ENVIRONMENT "STRATA_TEST_BIG_GDN=1;STRATA_CKPT_ASYNC=1;STRATA_CKPT_VRAM_SLOTS=3" SKIP_RETURN_CODE 77 TIMEOUT 300)
add_executable(conversation_validation_test src/core/conversation_validation_test.cpp)
target_link_libraries(conversation_validation_test PRIVATE strata_engine)
add_test(NAME conversation_validation_test COMMAND conversation_validation_test)
set_tests_properties(conversation_validation_test PROPERTIES ENVIRONMENT "CUDA_VISIBLE_DEVICES=-1" TIMEOUT 120)
if(NOT STRATA_ENABLE_HIP AND UNIX AND NOT APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
add_executable(conversation_transfer_test src/core/conversation_validation_test.cpp)
target_compile_definitions(conversation_transfer_test PRIVATE CONVERSATION_TEST_TRANSFERS)
target_link_options(conversation_transfer_test PRIVATE
-Wl,--wrap=cudaMemcpy -Wl,--wrap=cudaMemcpyAsync -Wl,--wrap=cudaStreamSynchronize
-Wl,--wrap=cudaGetDevice -Wl,--wrap=cudaStreamCreateWithFlags
-Wl,--wrap=cudaDeviceSynchronize -Wl,--wrap=cudaGetLastError)
target_link_libraries(conversation_transfer_test PRIVATE strata_engine)
add_test(NAME conversation_transfer_test COMMAND conversation_transfer_test)
set_tests_properties(conversation_transfer_test PROPERTIES ENVIRONMENT "CUDA_VISIBLE_DEVICES=-1" TIMEOUT 120)
endif()
endif()
# `strata generate` (P2.S6). The FIRST user-facing program: everything under it is a component, and this is
# what composes them into a token. No tokenizer - the prompt arrives as ids - because Gate C1 compares logits
# against llama.cpp on the SAME ids and a tokenizer on one side of that comparison is a second variable.
# plan v0.3 P5: batched prompt processing (tensor-core GEMMs through cuBLAS).
add_library(strata_prefill STATIC src/prefill/gemm.cu src/prefill/kernels.cu src/prefill/prefill.cpp)
if(STRATA_ENABLE_HIP)
# #313: the RDNA3 WMMA GEMMs include hip_runtime.h: AMD builds only (a CUDA build never sees this file)
target_sources(strata_prefill PRIVATE src/prefill/wmma_gemm.cu)
endif()
target_include_directories(strata_prefill PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${_strata_gpu_include_directories})
target_link_libraries(strata_prefill PUBLIC strata_engine ${_strata_gpu_blas_target} ${_strata_gpu_runtime_target})
if(STRATA_ENABLE_CUDA)
# v100/gdn-chunk: the chunked Volta GDN prompt recurrence. The dispatch (src/prefill/kernels.cu) is
# Volta-only (cc 7.0) and gated on STRATA_EXPERIMENTAL_SM60; the path itself stays opt-in (STRATA_GDN_CHUNK).
target_sources(strata_prefill PRIVATE src/prefill/gdn_chunk.cu)
endif()
if(STRATA_ENABLE_HIP AND STRATA_HIPBLASLT_AVAILABLE)
target_compile_definitions(strata_prefill PRIVATE STRATA_HIPBLASLT_AVAILABLE=1)
target_link_libraries(strata_prefill PRIVATE roc::hipblaslt)
add_executable(tune_hipblaslt tools/hip/tune_hipblaslt.cpp)
target_link_libraries(tune_hipblaslt PRIVATE hip::host roc::hipblas roc::hipblaslt)
endif()
if(STRATA_ENABLE_HIP AND STRATA_ROCBLAS_AVAILABLE)
# the prompt path's rocBLAS solution table (STRATA_ROCBLAS_TUNING; tools/hip/tune_rocblas makes one)
target_compile_definitions(strata_prefill PRIVATE STRATA_ROCBLAS_AVAILABLE=1)
target_link_libraries(strata_prefill PRIVATE roc::rocblas)
add_executable(tune_rocblas tools/hip/tune_rocblas.cpp)
target_link_libraries(tune_rocblas PRIVATE hip::host roc::rocblas)
endif()
# DFlash2 drafter tools (not built by default: cmake --build <dir> --target dflash2_parity dflash2_gemm_bench dflash2_attn_bench): the drafter against
# the torch port on a synthetic case (needs the pack and a case file, tools/dflash2/mk_parity_case.py), and the timing of its Q8 GEMM and attention
add_executable(dflash2_parity EXCLUDE_FROM_ALL src/core/dflash2_parity.cpp)
target_include_directories(dflash2_parity PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(dflash2_parity PRIVATE strata_engine strata_kernels ${_strata_gpu_runtime_target})
add_executable(dflash2_gemm_bench EXCLUDE_FROM_ALL src/core/dflash2_gemm_bench.cpp)
target_include_directories(dflash2_gemm_bench PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(dflash2_gemm_bench PRIVATE strata_kernels ${_strata_gpu_runtime_target})
add_executable(dflash2_launch_bench EXCLUDE_FROM_ALL src/core/dflash2_launch_bench.cu)
target_include_directories(dflash2_launch_bench PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(dflash2_launch_bench PRIVATE ${_strata_gpu_runtime_target})
add_executable(dflash2_bw_bench EXCLUDE_FROM_ALL src/core/dflash2_bw_bench.cu)
target_include_directories(dflash2_bw_bench PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(dflash2_bw_bench PRIVATE ${_strata_gpu_runtime_target})
add_executable(dflash2_small_bench EXCLUDE_FROM_ALL src/core/dflash2_small_bench.cpp)
target_include_directories(dflash2_small_bench PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(dflash2_small_bench PRIVATE strata_kernels ${_strata_gpu_runtime_target})
add_executable(dflash2_attn_bench EXCLUDE_FROM_ALL src/core/dflash2_attn_bench.cpp)
target_include_directories(dflash2_attn_bench PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(dflash2_attn_bench PRIVATE strata_kernels ${_strata_gpu_runtime_target})
add_executable(strata src/program/generate.cpp)
strata_utf8_manifest(strata)
target_include_directories(strata PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(strata PRIVATE strata_engine strata_prefill strata_spec ${_strata_gpu_runtime_target})
if(WIN32 AND STRATA_ENABLE_HIP)
# 0.2.1.2: the ROCm math libraries live in <package>\rocm\bin, which is on PATH only when the launcher starts the engine; load them on first
# use (main() adds that folder to the DLL search first) so `strata.exe --version` works from the package folder. amdhip64_7.dll and the
# C++ runtime sit beside the exe (tools/hip/package_windows.py) and stay normal imports.
target_link_libraries(strata PRIVATE delayimp)
target_link_options(strata PRIVATE "LINKER:/DELAYLOAD:hipblas.dll" "LINKER:/DELAYLOAD:libhipblaslt.dll" "LINKER:/DELAYLOAD:rocblas.dll")
endif()
# NEW-MODELS-PLAN.md T1: Qwen3.6-35B-A3B's reference path (one GPU, the model resident, plain kernels), checked
# against llama.cpp by tools/qwen35/; the engine itself does not run the family yet
add_executable(strata-q36ref src/qwen35/q36ref.cu)
target_include_directories(strata-q36ref PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${_strata_gpu_include_directories})
target_link_libraries(strata-q36ref PRIVATE strata_kernels strata_warnings ${_strata_gpu_runtime_target})
# the engine path for the same family (serve protocol, prompt chunks, decode graph): src/qwen35/q36_engine.cu, a
# library that `strata` switches to for a qwen35moe model (the family switch at the top of its main) and that
# strata-q36 wraps on its own. Not on gfx906 (#3172, #3271): it calls cuBLAS entry points the wave64 compat layer does
# not map (cublasSgemm, cublasGemmStridedBatchedEx) and its kernels assume 32-lane warps; main() guards every use with
# STRATA_HAVE_QWEN35, so a gfx906 build links and runs the Qwen3.8 family, and `strata --families` says so
if(NOT STRATA_HIP_GFX906)
add_library(strata_qwen35 STATIC src/qwen35/q36_engine.cu)
target_include_directories(strata_qwen35 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include PRIVATE ${_strata_gpu_include_directories})
target_link_libraries(strata_qwen35 PRIVATE strata_prefill strata_kernels strata_warnings ${_strata_gpu_runtime_target})
if(STRATA_NATIVE_EXPERTS)
# the hybrid mode (experts that do not all fit in VRAM): the CPU expert pool on ggml-cpu's arithmetic
target_link_libraries(strata_qwen35 PRIVATE strata_kernels_cpu)
target_compile_definitions(strata_qwen35 PRIVATE STRATA_Q36_CPU_POOL=1)
endif()
add_executable(strata-q36 src/qwen35/q36_main.cpp)
strata_utf8_manifest(strata-q36)
target_link_libraries(strata-q36 PRIVATE strata_qwen35 ${_strata_gpu_runtime_target})
target_link_libraries(strata PRIVATE strata_qwen35)
target_compile_definitions(strata PRIVATE STRATA_HAVE_QWEN35=1)
endif()
# DeepSeek-V4.1-Flash: the third engine path (src/dsv41, from Project Maya's maya-ds4, MIT, third_party/project-maya).
# Its own executable for now (strata-dsv41); a family switch in `strata` comes after first light. OFF by default: it
# changes no existing target (the vendored kernels live in strata::dsv41k, so no symbol is shared with strata_kernels).
option(STRATA_BUILD_DSV41 "Build the DeepSeek-V4.1 engine path (strata-dsv41)" OFF)
if(STRATA_BUILD_DSV41 AND NOT STRATA_HIP_GFX906)
add_library(strata_dsv41_kernels STATIC src/dsv41/vendor/exl3.cu src/dsv41/vendor/sampler.cu src/dsv41/vendor/moe_win.cu
src/dsv41/vendor/exl3_cpu.cpp)
if(NOT STRATA_ENABLE_HIP) # (the pre-sm_80 NVIDIA decode kernels exl3.cu dispatches to)
target_sources(strata_dsv41_kernels PRIVATE src/dsv41/vendor/exl3_legacy.cu)
endif()
target_include_directories(strata_dsv41_kernels PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${_strata_gpu_include_directories})
target_link_libraries(strata_dsv41_kernels PUBLIC ${_strata_gpu_runtime_target})
add_executable(strata-dsv41 src/dsv41/ds4_main.cpp src/dsv41/ds4_model.cu src/dsv41/ds4_kernels.cu src/dsv41/ds4_vision.cu
src/dsv41/ds4_gguf.cu src/dsv41/ds4_gguf_cpu.cpp src/dsv41/ds4_iq.cu src/dsv41/ds4_iq_cpu.cpp
src/dsv41/ds4_tq.cu src/dsv41/ds4_tq_cpu.cpp src/dsv41/ds4_gg_cpu.cpp src/dsv41/ds4_packinfo.cpp)
# the commit the binary was built from, readable with --version (set -DSTRATA_BUILD_ID=... for a tree without .git)
if(NOT STRATA_BUILD_ID AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/BUILD_ID) # (a git archive has no .git: the packer writes the commit here)
file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/BUILD_ID STRATA_BUILD_ID LIMIT_COUNT 1)
endif()
if(NOT STRATA_BUILD_ID)
execute_process(COMMAND git -C ${CMAKE_CURRENT_SOURCE_DIR} rev-parse --short=9 HEAD OUTPUT_VARIABLE STRATA_BUILD_ID
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
endif()
if(NOT STRATA_BUILD_ID)
set(STRATA_BUILD_ID unknown)
endif()
set_source_files_properties(src/dsv41/ds4_main.cpp PROPERTIES COMPILE_DEFINITIONS "DS4_BUILD_ID=\"${STRATA_BUILD_ID}\"")
target_include_directories(strata-dsv41 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${_strata_gpu_include_directories}
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ggml ${CMAKE_CURRENT_SOURCE_DIR}/src/prefill)
# strata_prefill: the RDNA3 WMMA dense GEMM (src/prefill/wmma_gemm.cu), which gemm() prefers over hipBLAS on gfx11 (measured
# 4.97 -> 20-40 TFLOPS on this part: the chunked prompt's dense GEMM is 11.1 s of a 4K prompt at the library's rate)
target_link_libraries(strata-dsv41 PRIVATE strata_prefill strata_dsv41_kernels ${_strata_gpu_runtime_target} ${_strata_gpu_blas_target})
find_package(Threads REQUIRED)
target_link_libraries(strata-dsv41 PRIVATE Threads::Threads)
if(WIN32 AND STRATA_ENABLE_HIP) # cudaMemGetInfo is the DXGI-budgeted hip_compat::mem_get_info (src/core/device.cu): on a Strix Halo the
# carve-out is not all the GPU may use, and the engine sizes its expert tiers by what the budget says is free
target_link_libraries(strata-dsv41 PRIVATE strata_core)
endif()
if(STRATA_NATIVE_EXPERTS) # Strata's CPU trellis kernels (tq_cpu.hpp) for the trellis experts on the CPU lane
target_link_libraries(strata-dsv41 PRIVATE strata_kernels_cpu)
target_compile_definitions(strata-dsv41 PRIVATE STRATA_DSV41_TQ_CPU=1)
endif()
if(STRATA_NATIVE_EXPERTS)
# the CPU lane's ggml-type rows: ggml-cpu's dots against Strata's AVX2 i-quant rows (bits and GB/s); no GPU, no model
add_executable(ds4_gg_bench tests/dsv41/gg_dot_bench.cpp src/dsv41/ds4_gg_cpu.cpp)
target_include_directories(ds4_gg_bench PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/src/dsv41
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ggml)
target_link_libraries(ds4_gg_bench PRIVATE strata_kernels_cpu Threads::Threads)
target_compile_definitions(ds4_gg_bench PRIVATE STRATA_DSV41_TQ_CPU=1)
endif()
if(STRATA_NATIVE_EXPERTS)
# the trellis experts (device kernels ds4_tq.cu, CPU lane ds4_tq_cpu.cpp) against a double reference; needs a GPU, no model
# both test .cpp files launch kernels, so they compile as HIP on the AMD backend and as CUDA on the NVIDIA one
if(STRATA_ENABLE_HIP)
set(_ds4_test_lang HIP)
elseif(STRATA_ENABLE_CUDA)
set(_ds4_test_lang CUDA)
else()
set(_ds4_test_lang CXX)
endif()
set_source_files_properties(tests/dsv41/tq_expert_test.cpp PROPERTIES LANGUAGE ${_ds4_test_lang})
add_executable(ds4_tq_test tests/dsv41/tq_expert_test.cpp src/dsv41/ds4_tq.cu src/dsv41/ds4_iq.cu src/dsv41/ds4_tq_cpu.cpp
src/dsv41/ds4_iq_cpu.cpp src/dsv41/ds4_gg_cpu.cpp tools/trellis/tqenc.cpp)
target_include_directories(ds4_tq_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${_strata_gpu_include_directories}
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ggml ${CMAKE_CURRENT_SOURCE_DIR}/src/kernels/cpu)
target_link_libraries(ds4_tq_test PRIVATE strata_kernels_cpu ${_strata_gpu_runtime_target} Threads::Threads)
target_compile_definitions(ds4_tq_test PRIVATE STRATA_DSV41_TQ_CPU=1)
# the same with the plain-FMA tile instead of the gfx12 WMMA one (what CUDA and gfx11 builds run)
add_executable(ds4_tq_test_portable tests/dsv41/tq_expert_test.cpp src/dsv41/ds4_tq.cu src/dsv41/ds4_iq.cu src/dsv41/ds4_tq_cpu.cpp
src/dsv41/ds4_iq_cpu.cpp src/dsv41/ds4_gg_cpu.cpp tools/trellis/tqenc.cpp)
target_include_directories(ds4_tq_test_portable PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${_strata_gpu_include_directories}
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ggml ${CMAKE_CURRENT_SOURCE_DIR}/src/kernels/cpu)
target_link_libraries(ds4_tq_test_portable PRIVATE strata_kernels_cpu ${_strata_gpu_runtime_target} Threads::Threads)
target_compile_definitions(ds4_tq_test_portable PRIVATE STRATA_DSV41_TQ_CPU=1 DS4_TQ_PORTABLE=1)
if(STRATA_ENABLE_HIP)
target_compile_options(ds4_tq_test_portable PRIVATE "$<$<COMPILE_LANGUAGE:HIP,CXX>:SHELL:-include ${CMAKE_CURRENT_SOURCE_DIR}/include/strata/dsv41/hip_extra.h>")
endif()
if(STRATA_ENABLE_HIP)
target_compile_options(ds4_tq_test PRIVATE "$<$<COMPILE_LANGUAGE:HIP,CXX>:SHELL:-include ${CMAKE_CURRENT_SOURCE_DIR}/include/strata/dsv41/hip_extra.h>")
endif()
# the i-quant experts (ds4_iq.cu) against ggml's own decode of the same blocks + a double reference, device and CPU
# lane, for every type the device takes: the gate a new type (IQ2_XS ..) has to pass before the engine uses it
set_source_files_properties(tests/dsv41/iq_expert_test.cpp PROPERTIES LANGUAGE ${_ds4_test_lang})
add_executable(ds4_iq_test tests/dsv41/iq_expert_test.cpp src/dsv41/ds4_iq.cu src/dsv41/ds4_iq_cpu.cpp
src/dsv41/ds4_gg_cpu.cpp src/dsv41/ds4_tq.cu src/dsv41/ds4_tq_cpu.cpp)
target_include_directories(ds4_iq_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include ${_strata_gpu_include_directories}
${CMAKE_CURRENT_SOURCE_DIR}/third_party/ggml ${CMAKE_CURRENT_SOURCE_DIR}/src/kernels/cpu)
target_link_libraries(ds4_iq_test PRIVATE strata_kernels_cpu ${_strata_gpu_runtime_target} Threads::Threads)
target_compile_definitions(ds4_iq_test PRIVATE STRATA_DSV41_TQ_CPU=1)
if(STRATA_ENABLE_HIP)
target_compile_options(ds4_iq_test PRIVATE "$<$<COMPILE_LANGUAGE:HIP,CXX>:SHELL:-include ${CMAKE_CURRENT_SOURCE_DIR}/include/strata/dsv41/hip_extra.h>")
endif()
# the kept conversations' .meta file and prefix matching (ds4_slotmeta.hpp): no GPU, no model
add_executable(ds4_slot_test tests/dsv41/slot_test.cpp)
target_include_directories(ds4_slot_test PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/dsv41)