From 22192214469f1510be42360f9abc0aca719ab574 Mon Sep 17 00:00:00 2001 From: Afloat Date: Wed, 11 Nov 2026 17:39:21 +0800 Subject: [PATCH] fix(cpu): cap generic thread defaults at the logical CPU count --- edge0-infer/docs/configuration.md | 4 ++-- edge0-infer/src/platform/backend.cpp | 3 ++- edge0-infer/tests/unit/platform/backend_test.cpp | 5 +++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/edge0-infer/docs/configuration.md b/edge0-infer/docs/configuration.md index 9b3749bc..79f036c8 100644 --- a/edge0-infer/docs/configuration.md +++ b/edge0-infer/docs/configuration.md @@ -31,8 +31,8 @@ The runtime opens a fixed set of devices (`devices`); a model can only use devic | Flag | Native field | Default | Meaning | | --- | --- | --- | --- | -| `--threads ` | `cpu_threads` | macOS 4; Android logical CPUs minus two (at least one); Windows `max(1, P - 2)` | CPU threads for decode; requires a CPU device; at most the logical CPU count | -| `--prefill-threads ` | `cpu_prefill_threads` | macOS 4; Android all logical CPUs; Windows `min(logical, P + 2)` | CPU threads for prefill (processing the input), set independently of decode | +| `--threads ` | `cpu_threads` | macOS/Linux up to 4 logical CPUs (at least one); Android logical CPUs minus two (at least one); Windows `max(1, P - 2)` | CPU threads for decode; requires a CPU device; at most the logical CPU count | +| `--prefill-threads ` | `cpu_prefill_threads` | macOS/Linux up to 4 logical CPUs (at least one); Android all logical CPUs; Windows `min(logical, P + 2)` | CPU threads for prefill (processing the input), set independently of decode | | `--memory-budget ` | `memory_budget_bytes` | The total memory of the smallest opened device | The total budget every loaded model and session is admitted against; must be positive and not exceed the device memory | The Android thread defaults were tuned on one device and may not suit other CPU layouts. On Windows, `P` is `logical / 2`. That rule assumes two threads per core and was measured on one 6-core CPU; set the counts explicitly when the CPU has no simultaneous multithreading or has efficiency cores. `info` prints the defaults; summaries report the resolved values and whether they were given explicitly. diff --git a/edge0-infer/src/platform/backend.cpp b/edge0-infer/src/platform/backend.cpp index 8ea6fb22..099f3265 100644 --- a/edge0-infer/src/platform/backend.cpp +++ b/edge0-infer/src/platform/backend.cpp @@ -55,7 +55,8 @@ CpuThreads default_cpu_threads(unsigned logical_cpus, bool android, bool windows const int physical = std::max(1, logical / 2); return {std::max(1, physical - 2), std::min(logical, physical + 2)}; } - return {4, 4}; + const int count = static_cast(std::max(1u, std::min(logical_cpus, 4u))); + return {count, count}; } CpuThreads default_cpu_threads() { #if defined(__ANDROID__) diff --git a/edge0-infer/tests/unit/platform/backend_test.cpp b/edge0-infer/tests/unit/platform/backend_test.cpp index 73834b06..830cc105 100644 --- a/edge0-infer/tests/unit/platform/backend_test.cpp +++ b/edge0-infer/tests/unit/platform/backend_test.cpp @@ -54,6 +54,11 @@ int main() { using edge0::platform::default_cpu_threads; const auto generic = default_cpu_threads(12, false); CHECK(generic.decode == 4 && generic.prefill == 4); + const std::pair generic_cases[] = {{0, 1}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {128, 4}}; + for (const auto& [logical, expected] : generic_cases) { + const auto limited = default_cpu_threads(logical, false); + CHECK(limited.decode == expected && limited.prefill == expected); + } const auto android = default_cpu_threads(8, true); CHECK(android.decode == 6 && android.prefill == 8); // Windows rule: physical = logical / 2; decode = max(1, physical - 2),