@@ -57,7 +57,9 @@ This is a C++ source compatibility change. It intentionally turns ambiguous
5757calls into compile errors instead of silently choosing the wrong behavior.
5858
5959CPU binding is implemented on Linux excluding Android. On unsupported
60- platforms, the binding flag does not change thread affinity.
60+ platforms, the binding flag does not change thread affinity. Linux binding
61+ enumerates the calling thread's allowed CPU mask, so non-zero-based and
62+ restricted cpusets are honored. Affinity API failures are logged.
6163
6264## DB global query and optimize pools
6365
@@ -66,8 +68,8 @@ initialized independently from `CgroupUtil::getCpuLimit()`:
6668
6769| Configuration | Default count | Default binding |
6870| ---| ---:| ---|
69- | Query pool | Cgroup CPU limit | Disabled |
70- | Optimize pool | Cgroup CPU limit | Disabled |
71+ | Query pool | Cgroup CPU limit | Enabled |
72+ | Optimize pool | Cgroup CPU limit | Enabled |
7173
7274Therefore:
7375
@@ -76,6 +78,8 @@ Therefore:
7678 CPU capacity.
7779- In a CPU-limited container, the DB defaults may differ from
7880 ` std::thread::hardware_concurrency() ` .
81+ - Cgroup v2 ` cpu.max ` values are parsed as ` quota period ` ; ` max period ` is
82+ treated as unlimited and falls back to the online CPU count.
7983- Query and optimize counts are independent. Setting ` query_threads ` does not
8084 implicitly change ` optimize_threads ` .
8185
@@ -85,7 +89,9 @@ runtime behavior was hardware-concurrency threads with binding enabled. This
8589meant configured values such as ` optimize_threads=1 ` were ignored.
8690
8791After this fix, the configured count and binding are both forwarded
88- explicitly, so the effective behavior matches the configuration.
92+ explicitly, so the effective behavior matches the configuration. Binding
93+ remains enabled by default to preserve the previous effective behavior; users
94+ can explicitly disable it when scheduler-managed placement is preferable.
8995
9096## Python API
9197
@@ -106,21 +112,21 @@ Behavior:
106112| ---| ---|
107113| Count is ` None ` | Use the corresponding cgroup-derived DB default |
108114| Count is a positive integer | Use exactly that many threads |
109- | Binding is ` None ` | Use the DB default (` False ` ) |
115+ | Binding is ` None ` | Use the DB default (` True ` ) |
110116| Binding is ` False ` | Do not bind worker threads |
111117| Binding is ` True ` | Bind worker threads where supported |
112118
113119Examples:
114120
115121``` python
116- # Both pools use their cgroup-derived counts without binding.
122+ # Both pools use their cgroup-derived counts with binding.
117123zvec.init()
118124
119- # Optimize uses exactly one unbound worker.
125+ # Optimize uses exactly one bound worker.
120126zvec.init(optimize_threads = 1 )
121127
122- # Query uses four bound workers; optimize keeps its independent defaults .
123- zvec.init(query_threads = 4 , query_thread_binding = True )
128+ # Explicitly let the OS schedule both pools without affinity pinning .
129+ zvec.init(query_thread_binding = False , optimize_thread_binding = False )
124130```
125131
126132## C API
@@ -134,7 +140,7 @@ zvec_config_data_set_optimize_thread_binding(config, binding);
134140zvec_config_data_get_optimize_thread_binding(config);
135141```
136142
137- If these setters are not called, binding remains disabled .
143+ If these setters are not called, binding remains enabled .
138144
139145## Internal call-site migration
140146
@@ -146,7 +152,8 @@ behavior:
146152- Recall tools with no requested count still use hardware concurrency with
147153 binding.
148154- Explicit benchmark and recall thread counts are now honored.
149- - `Index::Merge` uses `write_concurrency` without binding.
155+ - `Index::Merge` uses `write_concurrency` with binding, preserving its previous
156+ effective affinity behavior.
150157- DB query and optimize pools use their configured count and binding.
151158- Existing call sites already passing both count and binding are unchanged.
152159
@@ -165,7 +172,10 @@ ThreadPool(threads, true);
165172## Performance validation
166173
167174The fix was validated with an end-to-end Python DB API benchmark using the
168- SIFT dataset:
175+ SIFT dataset. The measured post-fix revision used disabled binding defaults.
176+ The current compatibility-preserving defaults keep binding enabled, so the
177+ post-fix placement measured below is reproduced by explicitly setting both
178+ binding options to `False`.
169179
170180| Parameter | Value |
171181|---|---|
@@ -192,7 +202,18 @@ The test machine exposes 64 logical CPUs. The benchmark called:
192202zvec.init(optimize_threads=1)
193203```
194204
195- It did not override the query thread count or either binding option.
205+ It did not override the query thread count or either binding option. In that
206+ measured revision the effective binding defaults were ` False ` ; with the
207+ current defaults, use the following equivalent configuration for the post-fix
208+ run:
209+
210+ ``` python
211+ zvec.init(
212+ optimize_threads = 1 ,
213+ query_thread_binding = False ,
214+ optimize_thread_binding = False ,
215+ )
216+ ```
196217
197218| Pool / behavior | Before the fix | After the fix |
198219| ---| ---| ---|
@@ -301,13 +322,17 @@ Behavior is unchanged for:
301322- `ThreadPool()`;
302323- `ThreadPool(n, false)`;
303324- `ThreadPool(n, true)`;
325+ - `Index::Merge` CPU binding;
326+ - DB query and optimize pool binding defaults;
304327- internal call sites whose old behavior was already explicit and correct.
305328
306329Behavior intentionally changes for:
307330
308331- one-argument thread counts that were previously interpreted as booleans;
309332- DB thread-count configuration that was previously ignored;
310- - `Index::Merge` and recall resize paths affected by ambiguous or reversed
311- arguments;
312- - DB binding defaults, which now explicitly default to disabled instead of
313- being accidentally enabled by overload resolution.
333+ - recall resize paths affected by reversed arguments.
334+
335+ DB binding remains enabled by default, matching the previous effective
336+ runtime behavior. Adding binding fields to the public C++ `ConfigData` struct
337+ changes its layout, so consumers linking across a C++ ABI boundary must rebuild
338+ against the updated headers.
0 commit comments