Skip to content

Commit 4aaa065

Browse files
weicjDavide Cifarelli
authored andcommitted
Add C++ backend-device placement foundation
The native server needs a placement contract that can carry target and draft backend/device choices into later mixed-backend and layer-split work without growing ad hoc CLI flags. Backend and GPU id are one placement concept, so the server now exposes device specs instead of separate backend and GPU options. This keeps the current native server behavior conservative: the parser accepts target and draft device specs, logs resolved placement, and rejects unsupported mixed-backend or target layer-split combinations until the runtime paths are implemented. Constraint: Native C++ server binaries are still compiled for one GPU backend at a time. Constraint: Target layer split remains rejected in the native server until the runtime path is wired. Rejected: Keep --target-backend plus --gpu as the long-term public surface | backend and device id should not be split across unrelated CLI flags. Confidence: high Scope-risk: low Tested: cmake --build dflash/build-cuda-draft-gpu-placement --target dflash_server -j 4 Tested: cmake --build dflash/build-hip-draft-gpu-placement --target dflash_server -j 4 Tested: dflash_server usage and placement-parse smoke checks Tested: git diff --check
1 parent 41a5bab commit 4aaa065

15 files changed

Lines changed: 294 additions & 49 deletions

dflash/src/common/backend_factory.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ std::unique_ptr<ModelBackend> create_backend(const BackendArgs & args) {
3737
cfg.target_path = args.model_path;
3838
cfg.draft_path = args.draft_path;
3939
cfg.device = args.device;
40-
cfg.draft_gpu = args.draft_gpu;
40+
cfg.draft_gpu = args.draft_device.gpu;
4141
cfg.stream_fd = args.stream_fd;
4242
cfg.fa_window = args.fa_window;
4343
cfg.kq_stride_pad = args.kq_stride_pad;

dflash/src/common/backend_factory.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
#pragma once
1212

1313
#include "model_backend.h"
14-
#include "device_placement.h"
14+
#include "placement/placement_config.h"
1515

1616
#include <memory>
1717
#include <string>
@@ -30,7 +30,7 @@ struct BackendArgs {
3030

3131
// Device placement
3232
DevicePlacement device;
33-
int draft_gpu = 0;
33+
DevicePlacement draft_device;
3434

3535
// I/O — only used when running under daemon_loop (legacy). The new
3636
// server passes -1 and uses on_token callbacks instead.
Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,6 @@
1-
// Device placement configuration for model backends.
2-
//
3-
// Describes which GPU(s) to use for a model. Supports:
4-
// - Single-GPU: just gpu field
5-
// - Multi-GPU layer-split: layer_split_gpus + optional weights
6-
// - Peer access between GPUs
1+
// Compatibility include for older common/device_placement.h users.
2+
// New C++ placement code should include placement/placement_config.h.
73

84
#pragma once
95

10-
#include <vector>
11-
12-
namespace dflash::common {
13-
14-
struct DevicePlacement {
15-
int gpu = 0; // primary GPU (single-GPU mode)
16-
17-
// Multi-GPU layer-split. Empty = single GPU mode.
18-
std::vector<int> layer_split_gpus; // GPU IDs for each shard
19-
std::vector<double> layer_split_weights; // proportional layer distribution (optional)
20-
21-
bool peer_access = false; // enable CUDA peer access between GPUs
22-
int max_ctx = 8192; // max KV cache context length
23-
24-
bool is_layer_split() const { return layer_split_gpus.size() > 1; }
25-
26-
int primary_gpu() const {
27-
return layer_split_gpus.empty() ? gpu : layer_split_gpus[0];
28-
}
29-
};
30-
31-
} // namespace dflash::common
6+
#include "placement/placement_config.h"

dflash/src/common/layer_split_utils.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
#pragma once
77

8-
#include "device_placement.h"
8+
#include "placement/placement_config.h"
99

1010
#include <string>
1111
#include <utility>

dflash/src/gemma4/gemma4_backend.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#pragma once
77

88
#include "common/model_backend.h"
9-
#include "common/device_placement.h"
9+
#include "placement/placement_config.h"
1010
#include "gemma4_internal.h"
1111
#include "common/sampler.h"
1212

dflash/src/gemma4/gemma4_daemon.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
#pragma once
44

5-
#include "device_placement.h"
5+
#include "placement/placement_config.h"
66
#include <string>
77

88
namespace dflash::common {

dflash/src/laguna/laguna_daemon.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
#pragma once
1515

16-
#include "device_placement.h"
16+
#include "placement/placement_config.h"
1717
#include <string>
1818
#include "ggml.h"
1919

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Backend placement identifiers shared by C++ server/runtime code.
2+
3+
#pragma once
4+
5+
#include <string>
6+
7+
namespace dflash::common {
8+
9+
enum class PlacementBackend {
10+
Auto,
11+
Cuda,
12+
Hip,
13+
};
14+
15+
inline const char * placement_backend_name(PlacementBackend backend) {
16+
switch (backend) {
17+
case PlacementBackend::Auto: return "auto";
18+
case PlacementBackend::Cuda: return "cuda";
19+
case PlacementBackend::Hip: return "hip";
20+
}
21+
return "auto";
22+
}
23+
24+
inline bool parse_placement_backend(const std::string & value,
25+
PlacementBackend & out) {
26+
if (value == "auto") {
27+
out = PlacementBackend::Auto;
28+
return true;
29+
}
30+
if (value == "cuda") {
31+
out = PlacementBackend::Cuda;
32+
return true;
33+
}
34+
if (value == "hip") {
35+
out = PlacementBackend::Hip;
36+
return true;
37+
}
38+
return false;
39+
}
40+
41+
inline PlacementBackend compiled_placement_backend() {
42+
#if defined(DFLASH27B_BACKEND_HIP) || defined(GGML_USE_HIP)
43+
return PlacementBackend::Hip;
44+
#else
45+
return PlacementBackend::Cuda;
46+
#endif
47+
}
48+
49+
inline bool placement_backend_supported(PlacementBackend backend) {
50+
return backend == PlacementBackend::Auto ||
51+
backend == compiled_placement_backend();
52+
}
53+
54+
} // namespace dflash::common
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Device placement configuration for model backends.
2+
//
3+
// Describes which backend device(s) to use for a model. Supports:
4+
// - Single-GPU: backend + gpu fields, exposed as cuda:0 / hip:0 / auto:0
5+
// - Multi-GPU layer-split: one backend + layer_split_gpus + optional weights
6+
// - Peer access between GPUs
7+
8+
#pragma once
9+
10+
#include "placement_backend.h"
11+
12+
#include <cstdlib>
13+
#include <string>
14+
#include <vector>
15+
16+
namespace dflash::common {
17+
18+
struct DevicePlacement {
19+
PlacementBackend backend = PlacementBackend::Auto;
20+
int gpu = 0; // primary GPU (single-GPU mode)
21+
22+
// Multi-GPU layer-split. Empty = single GPU mode.
23+
std::vector<int> layer_split_gpus; // GPU IDs for each shard
24+
std::vector<double> layer_split_weights; // proportional layer distribution (optional)
25+
26+
bool peer_access = false; // enable CUDA/HIP peer access between GPUs
27+
int max_ctx = 8192; // max KV cache context length
28+
29+
bool is_layer_split() const { return layer_split_gpus.size() > 1; }
30+
31+
int primary_gpu() const {
32+
return layer_split_gpus.empty() ? gpu : layer_split_gpus[0];
33+
}
34+
};
35+
36+
inline std::string placement_device_name(const DevicePlacement & device) {
37+
return std::string(placement_backend_name(device.backend)) + ":" +
38+
std::to_string(device.primary_gpu());
39+
}
40+
41+
inline bool parse_placement_device(const std::string & value,
42+
DevicePlacement & out) {
43+
const std::size_t sep = value.find(':');
44+
if (sep == std::string::npos || sep == 0 || sep + 1 >= value.size()) {
45+
return false;
46+
}
47+
48+
PlacementBackend backend = PlacementBackend::Auto;
49+
if (!parse_placement_backend(value.substr(0, sep), backend)) {
50+
return false;
51+
}
52+
53+
const std::string gpu_text = value.substr(sep + 1);
54+
char * end = nullptr;
55+
long gpu = std::strtol(gpu_text.c_str(), &end, 10);
56+
if (end == gpu_text.c_str() || *end != '\0' || gpu < 0) {
57+
return false;
58+
}
59+
60+
out.backend = backend;
61+
out.gpu = static_cast<int>(gpu);
62+
out.layer_split_gpus.clear();
63+
out.layer_split_weights.clear();
64+
return true;
65+
}
66+
67+
inline bool parse_placement_device_list(const std::string & value,
68+
DevicePlacement & out) {
69+
if (value.empty()) return false;
70+
71+
std::vector<int> gpus;
72+
PlacementBackend backend = PlacementBackend::Auto;
73+
bool have_backend = false;
74+
75+
std::size_t begin = 0;
76+
while (begin < value.size()) {
77+
const std::size_t end = value.find(',', begin);
78+
const std::string item = value.substr(
79+
begin,
80+
end == std::string::npos ? std::string::npos : end - begin);
81+
if (item.empty()) return false;
82+
83+
DevicePlacement parsed;
84+
if (!parse_placement_device(item, parsed)) return false;
85+
if (!have_backend) {
86+
backend = parsed.backend;
87+
have_backend = true;
88+
} else if (parsed.backend != backend) {
89+
return false;
90+
}
91+
gpus.push_back(parsed.gpu);
92+
93+
if (end == std::string::npos) break;
94+
begin = end + 1;
95+
}
96+
97+
if (gpus.empty()) return false;
98+
out.backend = backend;
99+
out.gpu = gpus[0];
100+
out.layer_split_gpus = gpus.size() > 1 ? gpus : std::vector<int>{};
101+
out.layer_split_weights.clear();
102+
return true;
103+
}
104+
105+
} // namespace dflash::common

dflash/src/qwen3/qwen3_backend.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#pragma once
1313

1414
#include "common/model_backend.h"
15-
#include "common/device_placement.h"
15+
#include "placement/placement_config.h"
1616
#include "qwen3_drafter_model.h"
1717
#include "qwen3_drafter.h"
1818
#include "common/sampler.h"

0 commit comments

Comments
 (0)