Summary
At pin 5717f6855a73a9ab7524748e2a10a57ed865737d,
bindings/c/src/threadpool.hpp:161 references OMPThreadPool
unconditionally inside a switch on svs_threadpool_kind:
// bindings/c/src/threadpool.hpp:155-169 (approximate)
svs::threads::ThreadPoolHandle build() const {
using namespace svs::threads;
switch (kind) {
case SVS_THREADPOOL_KIND_NATIVE:
return ThreadPoolHandle(NativeThreadPool(num_threads));
case SVS_THREADPOOL_KIND_OMP:
return ThreadPoolHandle(OMPThreadPool(num_threads)); // <-- always compiled
case SVS_THREADPOOL_KIND_SINGLE_THREAD:
return ThreadPoolHandle(SequentialThreadPool());
case SVS_THREADPOOL_KIND_CUSTOM:
return ThreadPoolHandle(CustomThreadPool{user_ops_, user_self_});
default:
throw std::invalid_argument("Unknown svs_threadpool_kind value.");
}
}
OMPThreadPool is defined at include/svs/lib/threads/threadpool.h:291-311
inside #if SVS_OMP. When the C API is compiled with SVS_OMP=0,
OMPThreadPool does not exist and this switch case fails to compile.
As a result, SVS_OMP=1 is currently required to build the C API,
which in turn forces libgomp.so.1 into the module's DT_NEEDED set
even when the caller never uses SVS_THREADPOOL_KIND_OMP at runtime.
This ask wraps the SVS_THREADPOOL_KIND_OMP case in #if SVS_OMP so
consumers who install a SVS_THREADPOOL_KIND_CUSTOM threadpool can
build without OpenMP entirely.
Motivation
valkey-search's SVS integration installs a custom threadpool
(SVS_THREADPOOL_KIND_CUSTOM) that returns size() = 1 and runs
parallel_for sequentially on the caller's reader thread. This is
the concurrency model documented in the valkey-search RFC and matches
HNSW's shape (throughput scales via concurrent searches at a higher
level, not multi-threading inside a single search). Under this model,
the OMPThreadPool code path is dead at runtime -- no omp_* symbol
is ever invoked -- but its symbols must still resolve at link time
because the switch case is unconditional.
Consequence: libgomp.so.1 appears in the module's DT_NEEDED and
must be added to ci/check_module_allocators.sh's ALLOWED_NEEDED
list. It is dead weight for our deployment: it adds a runtime
dependency the module never exercises.
Guarding the case behind #if SVS_OMP is a mechanical change that
lets us compile the C API with SVS_OMP=0 and drop the libgomp
dependency entirely.
Proposed change
Two-line edit in bindings/c/src/threadpool.hpp:
svs::threads::ThreadPoolHandle build() const {
using namespace svs::threads;
switch (kind) {
case SVS_THREADPOOL_KIND_NATIVE:
return ThreadPoolHandle(NativeThreadPool(num_threads));
#if SVS_OMP
case SVS_THREADPOOL_KIND_OMP:
return ThreadPoolHandle(OMPThreadPool(num_threads));
#endif
case SVS_THREADPOOL_KIND_SINGLE_THREAD:
return ThreadPoolHandle(SequentialThreadPool());
case SVS_THREADPOOL_KIND_CUSTOM:
return ThreadPoolHandle(CustomThreadPool{user_ops_, user_self_});
default:
throw std::invalid_argument("Unknown svs_threadpool_kind value.");
}
}
Behavior when compiled with SVS_OMP=0:
SVS_THREADPOOL_KIND_OMP becomes an unknown value at runtime and
falls through to the default clause, which already throws
std::invalid_argument.
- The enum value in the C API header can stay (backward-compatible),
but callers passing it against an SVS_OMP=0 build will get a
clear runtime error rather than a compile failure elsewhere.
Optionally, expose the compile-time OMP status via a macro or a
runtime probe (e.g., bool svs_has_omp_support(void)) so callers can
detect at runtime whether SVS_THREADPOOL_KIND_OMP is available.
Not strictly required.
Acceptance criteria
svs_c_api builds with SVS_ENABLE_OMP=0 on Linux without
compile errors. find_package(OpenMP REQUIRED) in the C API
CMake becomes conditional on SVS_ENABLE_OMP.
- A built-with-
SVS_OMP=0 library, when linked into a consumer,
has no omp_* symbol references (nm grep clean).
- Runtime:
svs_index_builder_set_threadpool(builder, SVS_THREADPOOL_KIND_OMP, ...)
on a SVS_OMP=0 build returns a std::invalid_argument
through the C-API's error handle (either via the default clause
or an explicit "OMP support not compiled in" message).
- All other threadpool kinds (NATIVE, SINGLE_THREAD, CUSTOM) work
identically to today's behavior under both SVS_OMP=0 and
SVS_OMP=1.
- Existing tests in
bindings/c/tests/ pass under both settings.
Non-goals
- Removing OMP support entirely. Consumers who want native OMP
parallelism inside SVS keep it via SVS_OMP=1.
- Changing the
NativeThreadPool behavior (which uses std::thread,
not OpenMP).
Summary
At pin
5717f6855a73a9ab7524748e2a10a57ed865737d,bindings/c/src/threadpool.hpp:161referencesOMPThreadPoolunconditionally inside a
switchonsvs_threadpool_kind:OMPThreadPoolis defined atinclude/svs/lib/threads/threadpool.h:291-311inside
#if SVS_OMP. When the C API is compiled withSVS_OMP=0,OMPThreadPooldoes not exist and this switch case fails to compile.As a result,
SVS_OMP=1is currently required to build the C API,which in turn forces
libgomp.so.1into the module'sDT_NEEDEDseteven when the caller never uses
SVS_THREADPOOL_KIND_OMPat runtime.This ask wraps the
SVS_THREADPOOL_KIND_OMPcase in#if SVS_OMPsoconsumers who install a
SVS_THREADPOOL_KIND_CUSTOMthreadpool canbuild without OpenMP entirely.
Motivation
valkey-search's SVS integration installs a custom threadpool
(
SVS_THREADPOOL_KIND_CUSTOM) that returnssize() = 1and runsparallel_forsequentially on the caller's reader thread. This isthe concurrency model documented in the valkey-search RFC and matches
HNSW's shape (throughput scales via concurrent searches at a higher
level, not multi-threading inside a single search). Under this model,
the
OMPThreadPoolcode path is dead at runtime -- noomp_*symbolis ever invoked -- but its symbols must still resolve at link time
because the switch case is unconditional.
Consequence:
libgomp.so.1appears in the module'sDT_NEEDEDandmust be added to
ci/check_module_allocators.sh'sALLOWED_NEEDEDlist. It is dead weight for our deployment: it adds a runtime
dependency the module never exercises.
Guarding the case behind
#if SVS_OMPis a mechanical change thatlets us compile the C API with
SVS_OMP=0and drop the libgompdependency entirely.
Proposed change
Two-line edit in
bindings/c/src/threadpool.hpp:Behavior when compiled with
SVS_OMP=0:SVS_THREADPOOL_KIND_OMPbecomes an unknown value at runtime andfalls through to the
defaultclause, which already throwsstd::invalid_argument.but callers passing it against an
SVS_OMP=0build will get aclear runtime error rather than a compile failure elsewhere.
Optionally, expose the compile-time OMP status via a macro or a
runtime probe (e.g.,
bool svs_has_omp_support(void)) so callers candetect at runtime whether
SVS_THREADPOOL_KIND_OMPis available.Not strictly required.
Acceptance criteria
svs_c_apibuilds withSVS_ENABLE_OMP=0on Linux withoutcompile errors.
find_package(OpenMP REQUIRED)in the C APICMake becomes conditional on
SVS_ENABLE_OMP.SVS_OMP=0library, when linked into a consumer,has no
omp_*symbol references (nmgrep clean).svs_index_builder_set_threadpool(builder, SVS_THREADPOOL_KIND_OMP, ...)on a
SVS_OMP=0build returns astd::invalid_argumentthrough the C-API's error handle (either via the
defaultclauseor an explicit "OMP support not compiled in" message).
identically to today's behavior under both
SVS_OMP=0andSVS_OMP=1.bindings/c/tests/pass under both settings.Non-goals
parallelism inside SVS keep it via
SVS_OMP=1.NativeThreadPoolbehavior (which uses std::thread,not OpenMP).