mirror of
https://github.com/leejet/stable-diffusion.cpp.git
synced 2026-11-11 19:36:33 +00:00
fix: size layer-split ranges in proportion to device capacity (#2309)
This commit is contained in:
1 parent
c44cbec722
commit
46da352af0
1 file changed
+61
-2
@@ -73,10 +73,13 @@ namespace sd {
|
|||||||
return backend_index == 0 ? primary_backend_vram_limit : 0;
|
return backend_index == 0 ? primary_backend_vram_limit : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Capacity of a device that reports no free memory and has no VRAM limit.
|
||||||
|
static constexpr int64_t graph_cut_layer_split_unknown_capacity = std::numeric_limits<int64_t>::max() / 4;
|
||||||
|
|
||||||
static std::vector<int64_t> graph_cut_layer_split_backend_capacities(const std::vector<ggml_backend_t>& backends,
|
static std::vector<int64_t> graph_cut_layer_split_backend_capacities(const std::vector<ggml_backend_t>& backends,
|
||||||
const std::vector<size_t>& backend_vram_limits,
|
const std::vector<size_t>& backend_vram_limits,
|
||||||
size_t primary_backend_vram_limit) {
|
size_t primary_backend_vram_limit) {
|
||||||
std::vector<int64_t> capacities(backends.size(), std::numeric_limits<int64_t>::max() / 4);
|
std::vector<int64_t> capacities(backends.size(), graph_cut_layer_split_unknown_capacity);
|
||||||
constexpr int64_t compute_headroom_bytes = 2ll * 1024 * 1024 * 1024;
|
constexpr int64_t compute_headroom_bytes = 2ll * 1024 * 1024 * 1024;
|
||||||
for (size_t i = 0; i < backends.size(); i++) {
|
for (size_t i = 0; i < backends.size(); i++) {
|
||||||
ggml_backend_dev_t dev = ggml_backend_get_device(backends[i]);
|
ggml_backend_dev_t dev = ggml_backend_get_device(backends[i]);
|
||||||
@@ -97,6 +100,57 @@ namespace sd {
|
|||||||
return capacities;
|
return capacities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whether filling the devices in order, each up to `fraction` of its capacity, places every
|
||||||
|
// segment. Mirrors the placement loop in partition_graph_cut_layer_split().
|
||||||
|
static bool graph_cut_layer_split_fits(const std::vector<int64_t>& segment_bytes,
|
||||||
|
const std::vector<int64_t>& capacities,
|
||||||
|
double fraction) {
|
||||||
|
size_t backend = 0;
|
||||||
|
int64_t used = 0;
|
||||||
|
for (int64_t bytes : segment_bytes) {
|
||||||
|
while (backend + 1 < capacities.size() && bytes > 0 &&
|
||||||
|
used + bytes > (int64_t)((double)capacities[backend] * fraction)) {
|
||||||
|
backend++;
|
||||||
|
used = 0;
|
||||||
|
}
|
||||||
|
if (bytes > 0 && used + bytes > (int64_t)((double)capacities[backend] * fraction)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
used += bytes;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill limits that size the contiguous ranges in proportion to the device capacities: the same
|
||||||
|
// fraction of every capacity, as small as possible while all segments still fit. Filling the
|
||||||
|
// first device up to its capacity would leave only the fixed headroom for compute, which is too
|
||||||
|
// small for large video graphs. A fraction of 1 is plain capacity-based placement, so every
|
||||||
|
// layout that fits the capacities is still found.
|
||||||
|
static std::vector<int64_t> graph_cut_layer_split_fill_limits(const std::vector<int64_t>& segment_bytes,
|
||||||
|
const std::vector<int64_t>& capacities) {
|
||||||
|
const bool capacities_known = std::all_of(capacities.begin(), capacities.end(), [](int64_t capacity) {
|
||||||
|
return capacity < graph_cut_layer_split_unknown_capacity;
|
||||||
|
});
|
||||||
|
if (!capacities_known || !graph_cut_layer_split_fits(segment_bytes, capacities, 1.0)) {
|
||||||
|
return capacities;
|
||||||
|
}
|
||||||
|
double low = 0.0;
|
||||||
|
double high = 1.0;
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
const double mid = (low + high) / 2;
|
||||||
|
if (graph_cut_layer_split_fits(segment_bytes, capacities, mid)) {
|
||||||
|
high = mid;
|
||||||
|
} else {
|
||||||
|
low = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<int64_t> limits(capacities.size());
|
||||||
|
for (size_t i = 0; i < capacities.size(); i++) {
|
||||||
|
limits[i] = (int64_t)((double)capacities[i] * high);
|
||||||
|
}
|
||||||
|
return limits;
|
||||||
|
}
|
||||||
|
|
||||||
bool partition_graph_cut_layer_split(const char* desc,
|
bool partition_graph_cut_layer_split(const char* desc,
|
||||||
ggml_cgraph* gf,
|
ggml_cgraph* gf,
|
||||||
const sd::ggml_graph_cut::Plan& plan,
|
const sd::ggml_graph_cut::Plan& plan,
|
||||||
@@ -151,6 +205,11 @@ namespace sd {
|
|||||||
return param_assignments.count(param) != 0;
|
return param_assignments.count(param) != 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Move to the next device at the proportional fill limit; the capacity stays the hard limit.
|
||||||
|
const std::vector<int64_t> backend_limits = reuse_assignments
|
||||||
|
? backend_capacities
|
||||||
|
: graph_cut_layer_split_fill_limits(segment_param_bytes, backend_capacities);
|
||||||
|
|
||||||
std::vector<ggml_backend_t> backend_by_segment(plan.segments.size(), split_backends[0]);
|
std::vector<ggml_backend_t> backend_by_segment(plan.segments.size(), split_backends[0]);
|
||||||
size_t current_backend = 0;
|
size_t current_backend = 0;
|
||||||
int64_t current_used = 0;
|
int64_t current_used = 0;
|
||||||
@@ -158,7 +217,7 @@ namespace sd {
|
|||||||
int64_t bytes = segment_param_bytes[seg_idx];
|
int64_t bytes = segment_param_bytes[seg_idx];
|
||||||
while (!reuse_assignments && current_backend + 1 < split_backends.size() &&
|
while (!reuse_assignments && current_backend + 1 < split_backends.size() &&
|
||||||
bytes > 0 &&
|
bytes > 0 &&
|
||||||
current_used + bytes > backend_capacities[current_backend]) {
|
current_used + bytes > backend_limits[current_backend]) {
|
||||||
current_backend++;
|
current_backend++;
|
||||||
current_used = 0;
|
current_used = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user