Skip to content

Commit 3b93a39

Browse files
committed
ggml : add CPU backend reference implementation
This commit introduces a CPU reference implementation for GGML, designed primarily for testing and validation purposes. The motivation for this addition is to have a pure C CPU backend implementation that does not use any hardware-specific optimizations or intrinsics. This will allow for testing the CPU backend variants against the reference implementation to ensure correctness Building: ```console $ cmake -B build \ -DGGML_CPU_REF_BACKEND=ON -DGGML_BACKEND_DL=ON \ -DGGML_CPU_ALL_VARIANTS=ON ``` List availble cpu architectures/variants: ```console $ ./build/bin/test-backend-ops cpu-variants --list CPU variants: CPU-haswell - 12th Gen Intel(R) Core(TM) i7-1260P CPU-sse42 - 12th Gen Intel(R) Core(TM) i7-1260P CPU-x64 - 12th Gen Intel(R) Core(TM) i7-1260P CPU-alderlake - 12th Gen Intel(R) Core(TM) i7-1260P CPU-sandybridge - 12th Gen Intel(R) Core(TM) i7-1260P ``` Run tests: ```console ./build-ref/bin/test-backend-ops cpu-variants --variant CPU-alderlake -o ADD CPU-ref features: SSE2 = 1 CPU-alderlake features: SSE2 = 1 SSE3 = 1 SSSE3 = 1 AVX = 1 AVX_VNNI = 1 AVX2 = 1 F16C = 1 FMA = 1 BMI2 = 1 LLAMAFILE = 1 OPENMP = 1 REPACK = 1 Testing CPU variant 'CPU-alderlake' against 'CPU-ref' backend... ADD(type=f16,ne=[1,1,8,1],nr=[1,1,1,1],nf=1): OK ADD(type=f16,ne=[1,1,1,1],nr=[32,1,1,1],nf=1): OK ... ```
1 parent 9aa6337 commit 3b93a39

File tree

10 files changed

+268
-8
lines changed

10 files changed

+268
-8
lines changed

ggml/CMakeLists.txt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,9 @@ option(GGML_HEXAGON "ggml: enable Hexagon backend"
257257
set (GGML_VULKAN_SHADERS_GEN_TOOLCHAIN "" CACHE FILEPATH "ggml: toolchain file for vulkan-shaders-gen")
258258

259259
# extra artifacts
260-
option(GGML_BUILD_TESTS "ggml: build tests" ${GGML_STANDALONE})
261-
option(GGML_BUILD_EXAMPLES "ggml: build examples" ${GGML_STANDALONE})
260+
option(GGML_BUILD_TESTS "ggml: build tests" ${GGML_STANDALONE})
261+
option(GGML_CPU_REF_BACKEND "ggml: build reference CPU backend for testing" OFF)
262+
option(GGML_BUILD_EXAMPLES "ggml: build examples" ${GGML_STANDALONE})
262263

263264
#
264265
# dependencies
@@ -288,7 +289,9 @@ add_subdirectory(src)
288289

289290
if (GGML_BUILD_TESTS)
290291
enable_testing()
291-
add_subdirectory(tests)
292+
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests")
293+
add_subdirectory(tests)
294+
endif ()
292295
endif ()
293296

294297
if (GGML_BUILD_EXAMPLES)

ggml/include/ggml-backend.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,9 @@ extern "C" {
245245
// Load all known backends from dynamic libraries
246246
GGML_API void ggml_backend_load_all(void);
247247
GGML_API void ggml_backend_load_all_from_path(const char * dir_path);
248+
// Load all variants for a backend and register them
249+
GGML_API void ggml_backend_load_all_variants(const char * name);
250+
GGML_API void ggml_backend_load_variant(const char * name, const char * variant);
248251

249252
//
250253
// Backend scheduler

ggml/include/ggml-cpu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ extern "C" {
7575
//
7676

7777
// x86
78+
GGML_BACKEND_API int ggml_cpu_has_sse2 (void);
7879
GGML_BACKEND_API int ggml_cpu_has_sse3 (void);
7980
GGML_BACKEND_API int ggml_cpu_has_ssse3 (void);
8081
GGML_BACKEND_API int ggml_cpu_has_avx (void);

ggml/src/CMakeLists.txt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,33 @@ ggml_add_backend(zDNN)
407407
ggml_add_backend(OpenCL)
408408
ggml_add_backend(Hexagon)
409409

410+
if (GGML_CPU_REF_BACKEND)
411+
if (NOT GGML_BACKEND_DL)
412+
message(FATAL_ERROR "GGML_CPU_REF_BACKEND requires GGML_BACKEND_DL")
413+
endif()
414+
set(GGML_SYSTEM_ARCH "cpu-ref")
415+
set(GGML_LLAMAFILE OFF)
416+
set(GGML_CPU_HBM OFF)
417+
set(GGML_CPU_REPACK OFF)
418+
set(GGML_OPENMP OFF)
419+
set(GGML_CPU_KLEIDIAI OFF)
420+
set(GGML_ACCELERATE OFF)
421+
422+
ggml_add_cpu_backend_variant(ref)
423+
424+
if (GGML_SYSTEM_ARCH MATCHES "arm|aarch64|ARM|AARCH64")
425+
target_compile_options(ggml-cpu-ref PRIVATE
426+
-U__ARM_NEON
427+
-U__ARM_FEATURE_FMA
428+
-U__ARM_FEATURE_FP16_VECTOR_ARITHMETIC
429+
-U__ARM_FEATURE_DOTPROD
430+
-U__ARM_FEATURE_MATMUL_INT8
431+
-U__ARM_FEATURE_SVE
432+
)
433+
endif()
434+
target_compile_definitions(ggml PRIVATE GGML_USE_CPU_REF)
435+
endif()
436+
410437
foreach (target ggml-base ggml)
411438
target_include_directories(${target} PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> $<INSTALL_INTERFACE:include>)
412439
target_compile_features (${target} PRIVATE c_std_11 cxx_std_17) # don't bump

ggml/src/ggml-backend-reg.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,4 +613,76 @@ void ggml_backend_load_all_from_path(const char * dir_path) {
613613
if (backend_path) {
614614
ggml_backend_load(backend_path);
615615
}
616+
#ifdef GGML_USE_CPU_REF
617+
ggml_backend_load_best("cpu-ref", silent, dir_path);
618+
#endif
619+
}
620+
621+
void ggml_backend_load_all_variants(const char * name) {
622+
// enumerate all the files that match [lib]ggml-name-*.[so|dll] in the search paths
623+
const fs::path name_path = fs::u8path(name);
624+
const fs::path file_prefix = backend_filename_prefix().native() + name_path.native() + fs::u8path("-").native();
625+
const fs::path file_extension = backend_filename_extension();
626+
627+
std::vector<fs::path> search_paths;
628+
#ifdef GGML_BACKEND_DIR
629+
search_paths.push_back(fs::u8path(GGML_BACKEND_DIR));
630+
#endif
631+
// default search paths: executable directory, current directory
632+
search_paths.push_back(get_executable_path());
633+
search_paths.push_back(fs::current_path());
634+
635+
for (const auto & search_path : search_paths) {
636+
if (!fs::exists(search_path)) {
637+
GGML_LOG_DEBUG("%s: search path %s does not exist\n", __func__, path_str(search_path).c_str());
638+
continue;
639+
}
640+
fs::directory_iterator dir_it(search_path, fs::directory_options::skip_permission_denied);
641+
for (const auto & entry : dir_it) {
642+
if (entry.is_regular_file()) {
643+
auto filename = entry.path().filename();
644+
auto ext = entry.path().extension();
645+
if (filename.native().find(file_prefix.native()) == 0 && ext == file_extension) {
646+
fs::path path = search_path / filename;
647+
ggml_backend_reg_t backend = get_reg().load_backend(path, false);
648+
if (backend == nullptr) {
649+
GGML_LOG_ERROR("%s: failed to load backend variant %s\n", __func__, path_str(entry.path()).c_str());
650+
}
651+
652+
}
653+
}
654+
}
655+
}
656+
}
657+
658+
void ggml_backend_load_variant(const char * name, const char * variant) {
659+
const fs::path name_path = fs::u8path(name);
660+
const fs::path variant_path = fs::u8path(variant);
661+
const fs::path file_prefix = backend_filename_prefix().native() + name_path.native() + fs::u8path("-").native();
662+
const fs::path target_filename = file_prefix.native() + variant_path.native() + backend_filename_extension().native();
663+
664+
std::vector<fs::path> search_paths;
665+
#ifdef GGML_BACKEND_DIR
666+
search_paths.push_back(fs::u8path(GGML_BACKEND_DIR));
667+
#endif
668+
// default search paths: executable directory, current directory
669+
search_paths.push_back(get_executable_path());
670+
search_paths.push_back(fs::current_path());
671+
672+
for (const auto & search_path : search_paths) {
673+
if (!fs::exists(search_path)) {
674+
GGML_LOG_DEBUG("%s: search path %s does not exist\n", __func__, path_str(search_path).c_str());
675+
continue;
676+
}
677+
678+
fs::path full_path = search_path / target_filename;
679+
if (fs::exists(full_path) && fs::is_regular_file(full_path)) {
680+
ggml_backend_reg_t backend = get_reg().load_backend(full_path, false);
681+
if (backend == nullptr) {
682+
GGML_LOG_ERROR("%s: failed to load backend variant %s\n", __func__, path_str(full_path).c_str());
683+
} else {
684+
return;
685+
}
686+
}
687+
}
616688
}

ggml/src/ggml-cpu/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ function(ggml_add_cpu_backend_variant_impl tag_name)
5252
target_compile_features(${GGML_CPU_NAME} PRIVATE c_std_11 cxx_std_17)
5353
target_include_directories(${GGML_CPU_NAME} PRIVATE . ggml-cpu)
5454

55+
if (tag_name)
56+
target_compile_definitions(${GGML_CPU_NAME} PRIVATE GGML_CPU_VARIANT_NAME="CPU-${tag_name}")
57+
else()
58+
target_compile_definitions(${GGML_CPU_NAME} PRIVATE GGML_CPU_VARIANT_NAME="CPU")
59+
endif()
60+
5561
if (APPLE AND GGML_ACCELERATE)
5662
find_library(ACCELERATE_FRAMEWORK Accelerate)
5763
if (ACCELERATE_FRAMEWORK)

ggml/src/ggml-cpu/ggml-cpu.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3447,6 +3447,14 @@ int ggml_cpu_has_llamafile(void) {
34473447
#endif
34483448
}
34493449

3450+
int ggml_cpu_has_sse2(void) {
3451+
#if defined(__SSE2__)
3452+
return 1;
3453+
#else
3454+
return 0;
3455+
#endif
3456+
}
3457+
34503458
int ggml_cpu_has_sse3(void) {
34513459
#if defined(__SSE3__)
34523460
return 1;

ggml/src/ggml-cpu/ggml-cpu.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ struct ggml_backend_cpu_context {
108108
};
109109

110110
static const char * ggml_backend_cpu_get_name(ggml_backend_t backend) {
111-
return "CPU";
111+
return GGML_CPU_VARIANT_NAME;
112112

113113
GGML_UNUSED(backend);
114114
}
@@ -337,7 +337,7 @@ struct ggml_backend_cpu_device_context {
337337
};
338338

339339
static const char * ggml_backend_cpu_device_get_name(ggml_backend_dev_t dev) {
340-
return "CPU";
340+
return GGML_CPU_VARIANT_NAME;
341341

342342
GGML_UNUSED(dev);
343343
}
@@ -516,6 +516,9 @@ static ggml_backend_feature * ggml_backend_cpu_get_features(ggml_backend_reg_t r
516516
ggml_cpu_init();
517517

518518
std::vector<ggml_backend_feature> features;
519+
if (ggml_cpu_has_sse2()) {
520+
features.push_back({ "SSE2", "1" });
521+
}
519522
if (ggml_cpu_has_sse3()) {
520523
features.push_back({ "SSE3", "1" });
521524
}

tests/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ if (NOT LLAMA_SANITIZE_ADDRESS)
202202
endif()
203203
llama_build_and_test(test-gguf.cpp)
204204
llama_build_and_test(test-backend-ops.cpp)
205+
target_sources(test-backend-ops PRIVATE ${PROJECT_SOURCE_DIR}/ggml/src/ggml.c)
206+
target_compile_definitions(test-backend-ops PRIVATE GGML_BUILD GGML_VERSION=\"${GGML_VERSION}\" GGML_COMMIT=\"${GGML_COMMIT}\")
207+
target_include_directories(test-backend-ops PRIVATE ${PROJECT_SOURCE_DIR}/ggml/src)
205208

206209
llama_build_and_test(test-model-load-cancel.cpp LABEL "model")
207210
llama_build_and_test(test-autorelease.cpp LABEL "model")

0 commit comments

Comments
 (0)