mirror of
https://github.com/leejet/stable-diffusion.cpp.git
synced 2026-09-24 20:20:37 +00:00
Compare commits
6 Commits
2ea8aff7ef
...
1330cebae8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1330cebae8 | ||
|
|
17860c0e45 | ||
|
|
275ab58e01 | ||
|
|
d32b4e893b | ||
|
|
9982c9caae | ||
|
|
3e037a81e4 |
@ -95,6 +95,8 @@ option(SD_MUSA "sd: musa backend" OFF)
|
|||||||
option(SD_BUILD_SHARED_LIBS "sd: build shared libs" OFF)
|
option(SD_BUILD_SHARED_LIBS "sd: build shared libs" OFF)
|
||||||
option(SD_BUILD_SHARED_GGML_LIB "sd: build ggml as a separate shared lib" OFF)
|
option(SD_BUILD_SHARED_GGML_LIB "sd: build ggml as a separate shared lib" OFF)
|
||||||
option(SD_USE_SYSTEM_GGML "sd: use system-installed GGML library" OFF)
|
option(SD_USE_SYSTEM_GGML "sd: use system-installed GGML library" OFF)
|
||||||
|
option(SD_USE_UPSTREAM_GGML "sd: build with upstream GGML instead of the patched GGML extensions" OFF)
|
||||||
|
set(SD_GGML_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ggml" CACHE PATH "sd: ggml source directory (also supplies private headers for system ggml)")
|
||||||
#option(SD_BUILD_SERVER "sd: build server example" ON)
|
#option(SD_BUILD_SERVER "sd: build server example" ON)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
@ -325,27 +327,19 @@ if (NOT SD_USE_SYSTEM_GGML)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# deps
|
# deps
|
||||||
# Only add ggml if it hasn't been added yet
|
include(cmake/ggml.cmake)
|
||||||
if (NOT TARGET ggml)
|
|
||||||
if (SD_USE_SYSTEM_GGML)
|
|
||||||
find_package(ggml REQUIRED)
|
|
||||||
if (NOT ggml_FOUND)
|
|
||||||
message(FATAL_ERROR "System-installed GGML library not found.")
|
|
||||||
endif()
|
|
||||||
add_library(ggml ALIAS ggml::ggml)
|
|
||||||
else()
|
|
||||||
add_subdirectory(ggml)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_subdirectory(thirdparty)
|
add_subdirectory(thirdparty)
|
||||||
|
|
||||||
target_sources(${SD_LIB} PRIVATE $<TARGET_OBJECTS:zip>)
|
target_sources(${SD_LIB} PRIVATE $<TARGET_OBJECTS:zip>)
|
||||||
target_link_libraries(${SD_LIB} PUBLIC ggml)
|
target_link_libraries(${SD_LIB} PUBLIC ggml)
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
target_link_libraries(${SD_LIB} PRIVATE Threads::Threads)
|
||||||
target_link_libraries(${SD_LIB} PRIVATE onig sd-utf8proc)
|
target_link_libraries(${SD_LIB} PRIVATE onig sd-utf8proc)
|
||||||
if (SD_CUDA)
|
if (SD_CUDA)
|
||||||
find_package(CUDAToolkit REQUIRED)
|
find_package(CUDAToolkit REQUIRED)
|
||||||
target_link_libraries(${SD_LIB} PRIVATE CUDA::cuda_driver)
|
# Keep the driver stub on downstream link lines when no driver is installed.
|
||||||
|
target_link_libraries(${SD_LIB} PUBLIC CUDA::cuda_driver)
|
||||||
set_property(SOURCE src/core/ggml_extend_backend.cpp APPEND PROPERTY COMPILE_DEFINITIONS SD_USE_CUDA)
|
set_property(SOURCE src/core/ggml_extend_backend.cpp APPEND PROPERTY COMPILE_DEFINITIONS SD_USE_CUDA)
|
||||||
endif()
|
endif()
|
||||||
target_include_directories(${SD_LIB} PUBLIC . src include)
|
target_include_directories(${SD_LIB} PUBLIC . src include)
|
||||||
|
|||||||
27
cmake/ggml.cmake
Normal file
27
cmake/ggml.cmake
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
if(NOT TARGET ggml AND NOT TARGET ggml::ggml)
|
||||||
|
if(SD_USE_SYSTEM_GGML)
|
||||||
|
find_package(ggml REQUIRED)
|
||||||
|
else()
|
||||||
|
add_subdirectory("${SD_GGML_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/ggml")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(NOT TARGET ggml)
|
||||||
|
add_library(ggml ALIAS ggml::ggml)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
get_target_property(sd_ggml_imported ggml IMPORTED)
|
||||||
|
if(sd_ggml_imported)
|
||||||
|
set(sd_ggml_private_include "${SD_GGML_SOURCE_DIR}/src")
|
||||||
|
else()
|
||||||
|
get_target_property(sd_ggml_private_include ggml SOURCE_DIR)
|
||||||
|
endif()
|
||||||
|
if(NOT EXISTS "${sd_ggml_private_include}/ggml-impl.h")
|
||||||
|
message(FATAL_ERROR "Set SD_GGML_SOURCE_DIR to the source tree matching the selected ggml library (ggml-impl.h is required).")
|
||||||
|
endif()
|
||||||
|
target_include_directories(${SD_LIB} PRIVATE "${sd_ggml_private_include}")
|
||||||
|
set_property(TARGET ${SD_LIB} PROPERTY SD_GGML_PRIVATE_INCLUDE_DIR "${sd_ggml_private_include}")
|
||||||
|
|
||||||
|
if(SD_USE_UPSTREAM_GGML)
|
||||||
|
target_compile_definitions(${SD_LIB} PUBLIC SD_USE_UPSTREAM_GGML)
|
||||||
|
message(WARNING "Using upstream GGML: FP8 and INT8 tensorwise/convrot are disabled. Some operators may be unsupported and performance may be lower than with patched GGML.")
|
||||||
|
endif()
|
||||||
@ -10,7 +10,8 @@ set(SD_BIN_DIR "@PACKAGE_SD_BIN_INSTALL_DIR@")
|
|||||||
|
|
||||||
include(CMakeFindDependencyMacro)
|
include(CMakeFindDependencyMacro)
|
||||||
find_dependency(ggml REQUIRED HINTS "${SD_LIB_DIR}/cmake")
|
find_dependency(ggml REQUIRED HINTS "${SD_LIB_DIR}/cmake")
|
||||||
if(@SD_CUDA@ AND NOT SD_SHARED_LIB)
|
find_dependency(Threads REQUIRED)
|
||||||
|
if(@SD_CUDA@)
|
||||||
find_dependency(CUDAToolkit REQUIRED)
|
find_dependency(CUDAToolkit REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -25,13 +26,13 @@ if(NOT TARGET stable-diffusion)
|
|||||||
set_target_properties(stable-diffusion
|
set_target_properties(stable-diffusion
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${SD_INCLUDE_DIR}"
|
INTERFACE_INCLUDE_DIRECTORIES "${SD_INCLUDE_DIR}"
|
||||||
INTERFACE_LINK_LIBRARIES "ggml::ggml"
|
INTERFACE_LINK_LIBRARIES "ggml::ggml;Threads::Threads"
|
||||||
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
||||||
IMPORTED_LOCATION "${stable-diffusion_LIBRARY}"
|
IMPORTED_LOCATION "${stable-diffusion_LIBRARY}"
|
||||||
INTERFACE_COMPILE_FEATURES "c_std_11;cxx_std_17"
|
INTERFACE_COMPILE_FEATURES "c_std_11;cxx_std_17"
|
||||||
POSITION_INDEPENDENT_CODE ON)
|
POSITION_INDEPENDENT_CODE ON)
|
||||||
|
|
||||||
if(@SD_CUDA@ AND NOT SD_SHARED_LIB)
|
if(@SD_CUDA@)
|
||||||
set_property(TARGET stable-diffusion APPEND PROPERTY INTERFACE_LINK_LIBRARIES CUDA::cuda_driver)
|
set_property(TARGET stable-diffusion APPEND PROPERTY INTERFACE_LINK_LIBRARIES CUDA::cuda_driver)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@ -7,5 +7,5 @@ Name: stable-diffusion
|
|||||||
Description: Diffusion model(SD,Flux,Wan,Qwen Image,Z-Image,...) inference in pure C/C++
|
Description: Diffusion model(SD,Flux,Wan,Qwen Image,Z-Image,...) inference in pure C/C++
|
||||||
Version: @SDCPP_BUILD_VERSION@
|
Version: @SDCPP_BUILD_VERSION@
|
||||||
Libs: -L${libdir} -lstable-diffusion
|
Libs: -L${libdir} -lstable-diffusion
|
||||||
Libs.private: -lggml -lggml-base
|
Libs.private: -lggml -lggml-base @CMAKE_THREAD_LIBS_INIT@
|
||||||
Cflags: -I${includedir}
|
Cflags: -I${includedir}
|
||||||
|
|||||||
@ -16,6 +16,38 @@ git submodule init
|
|||||||
git submodule update
|
git submodule update
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Selecting a GGML source tree
|
||||||
|
|
||||||
|
By default, sd.cpp builds the patched GGML submodule in `ggml/`. To build with
|
||||||
|
an upstream GGML checkout instead, enable `SD_USE_UPSTREAM_GGML` and set
|
||||||
|
`SD_GGML_SOURCE_DIR`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cmake -S . -B build-upstream -DSD_USE_UPSTREAM_GGML=ON -DSD_GGML_SOURCE_DIR=../ggml-upstream
|
||||||
|
cmake --build build-upstream --config Release
|
||||||
|
```
|
||||||
|
|
||||||
|
The selected source tree supplies both the library and its private headers.
|
||||||
|
Backend options such as `-DSD_CUDA=ON` apply to the selected tree as usual.
|
||||||
|
|
||||||
|
`SD_USE_UPSTREAM_GGML` defaults to `OFF`, which enables the patched GGML
|
||||||
|
extensions. Set it to `ON` when using upstream GGML; it selects the compatibility
|
||||||
|
mode and does not download or replace the GGML source tree. Upstream mode
|
||||||
|
disables FP8 and INT8 tensorwise/convrot and rejects their model files with an
|
||||||
|
explicit error. FP8 weight type requests, tensor type rules and conversion
|
||||||
|
outputs are also rejected; no automatic conversion is performed.
|
||||||
|
|
||||||
|
Upstream GGML may lack some operators and performance optimizations provided by
|
||||||
|
the patched version. A warning is emitted during CMake configuration and when
|
||||||
|
creating an inference context. Ordinary floating-point and shared GGML
|
||||||
|
quantization types remain available, subject to backend operator support.
|
||||||
|
|
||||||
|
`SD_USE_SYSTEM_GGML=ON` instead links an installed GGML CMake package, located
|
||||||
|
with `ggml_DIR` or `CMAKE_PREFIX_PATH`. In that mode, `SD_GGML_SOURCE_DIR` must
|
||||||
|
point to the matching source tree for private headers. The installed library
|
||||||
|
must use the same ABI settings as sd.cpp, including `GGML_MAX_NAME`.
|
||||||
|
Set `SD_USE_UPSTREAM_GGML=ON` as well if the installed package is upstream GGML.
|
||||||
|
|
||||||
## WebP and WebM Support in Examples
|
## WebP and WebM Support in Examples
|
||||||
|
|
||||||
The example applications (`examples/cli` and `examples/server`) use `libwebp` to support WebP image I/O, and `examples/cli` can also use `libwebm` for `.webm` video output. Both are enabled by default. WebM output currently reuses `libwebp` to encode each frame as VP8 before muxing with `libwebm`.
|
The example applications (`examples/cli` and `examples/server`) use `libwebp` to support WebP image I/O, and `examples/cli` can also use `libwebm` for `.webm` video output. Both are enabled by default. WebM output currently reuses `libwebp` to encode each frame as VP8 before muxing with `libwebm`.
|
||||||
|
|||||||
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
sd.cpp can load and execute ComfyUI `int8_tensorwise` safetensors with `convrot` metadata directly. The stored INT8 weights are not converted to another weight type at load time.
|
sd.cpp can load and execute ComfyUI `int8_tensorwise` safetensors with `convrot` metadata directly. The stored INT8 weights are not converted to another weight type at load time.
|
||||||
|
|
||||||
|
This requires the INT8 tensorwise/convrot extensions in the patched GGML.
|
||||||
|
Builds with `SD_USE_UPSTREAM_GGML=ON` reject these files during loading.
|
||||||
|
|
||||||
## Checkpoint format
|
## Checkpoint format
|
||||||
|
|
||||||
Each quantized linear module contains the following tensors:
|
Each quantized linear module contains the following tensors:
|
||||||
|
|||||||
@ -624,7 +624,7 @@ ArgOptions SDContextParams::get_options() {
|
|||||||
true, &diffusion_conv_direct},
|
true, &diffusion_conv_direct},
|
||||||
{"",
|
{"",
|
||||||
"--vae-conv-direct",
|
"--vae-conv-direct",
|
||||||
"use ggml_conv2d_direct in the vae model",
|
"use direct 2D and 3D convolutions in the vae model",
|
||||||
true, &vae_conv_direct},
|
true, &vae_conv_direct},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
2
ggml
2
ggml
@ -1 +1 @@
|
|||||||
Subproject commit 1e22ec0d04b43afa69963e4b4ea6683535d54d79
|
Subproject commit c6632cd905401abc58b6f5cdd52d228aa7ca1b88
|
||||||
@ -362,6 +362,9 @@ bool convert_with_components(const char* model_path,
|
|||||||
const char* tensor_type_rules,
|
const char* tensor_type_rules,
|
||||||
bool convert_name,
|
bool convert_name,
|
||||||
int n_threads) {
|
int n_threads) {
|
||||||
|
if (!validate_tensor_types(output_type, tensor_type_rules)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
ModelLoader model_loader;
|
ModelLoader model_loader;
|
||||||
bool loaded_any = false;
|
bool loaded_any = false;
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
#include "core/ggml_graph_cut.h"
|
#include "core/ggml_graph_cut.h"
|
||||||
#include "core/util.h"
|
#include "core/util.h"
|
||||||
#include "ggml-cpu.h"
|
#include "ggml-cpu.h"
|
||||||
#include "ggml/src/ggml-impl.h"
|
#include "ggml-impl.h"
|
||||||
|
|
||||||
namespace sd {
|
namespace sd {
|
||||||
ComputeWorkspace::~ComputeWorkspace() {
|
ComputeWorkspace::~ComputeWorkspace() {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include "core/ggml_extend.h"
|
#include "core/ggml_extend.h"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <stdexcept>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "core/ggml_extend_backend.h"
|
#include "core/ggml_extend_backend.h"
|
||||||
@ -247,6 +248,7 @@ ggml_tensor* ggml_ext_linear_i8_tensorwise(ggml_context* ctx,
|
|||||||
ggml_tensor* b,
|
ggml_tensor* b,
|
||||||
int convrot_group_size,
|
int convrot_group_size,
|
||||||
float scale) {
|
float scale) {
|
||||||
|
#ifndef SD_USE_UPSTREAM_GGML
|
||||||
GGML_ASSERT(x->type == GGML_TYPE_F32 || (x->type == GGML_TYPE_I8 && scale == 1.f));
|
GGML_ASSERT(x->type == GGML_TYPE_F32 || (x->type == GGML_TYPE_I8 && scale == 1.f));
|
||||||
if (scale != 1.f) {
|
if (scale != 1.f) {
|
||||||
x = ggml_ext_scale(ctx, x, scale);
|
x = ggml_ext_scale(ctx, x, scale);
|
||||||
@ -270,6 +272,16 @@ ggml_tensor* ggml_ext_linear_i8_tensorwise(ggml_context* ctx,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return x;
|
return x;
|
||||||
|
#else
|
||||||
|
GGML_UNUSED(ctx);
|
||||||
|
GGML_UNUSED(x);
|
||||||
|
GGML_UNUSED(w);
|
||||||
|
GGML_UNUSED(weight_scale);
|
||||||
|
GGML_UNUSED(b);
|
||||||
|
GGML_UNUSED(convrot_group_size);
|
||||||
|
GGML_UNUSED(scale);
|
||||||
|
throw std::runtime_error("INT8 tensorwise/convrot is not supported by this ggml build");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ggml_tensor* ggml_ext_pad_ext(ggml_context* ctx,
|
ggml_tensor* ggml_ext_pad_ext(ggml_context* ctx,
|
||||||
@ -452,8 +464,13 @@ ggml_tensor* ggml_ext_conv_3d(ggml_context* ctx,
|
|||||||
int d0,
|
int d0,
|
||||||
int d1,
|
int d1,
|
||||||
int d2,
|
int d2,
|
||||||
bool force_prec_f32) {
|
bool force_prec_f32,
|
||||||
if (force_prec_f32) {
|
bool direct) {
|
||||||
|
if (direct) {
|
||||||
|
int64_t OC = w->ne[3] / IC;
|
||||||
|
int64_t N = x->ne[3] / IC;
|
||||||
|
x = ggml_conv_3d_direct(ctx, w, x, s0, s1, s2, p0, p1, p2, d0, d1, d2, (int)IC, (int)N, (int)OC);
|
||||||
|
} else if (force_prec_f32) {
|
||||||
ggml_tensor* im2col = ggml_im2col_3d(ctx, w, x, IC, s0, s1, s2, p0, p1, p2, d0, d1, d2, w->type);
|
ggml_tensor* im2col = ggml_im2col_3d(ctx, w, x, IC, s0, s1, s2, p0, p1, p2, d0, d1, d2, w->type);
|
||||||
|
|
||||||
int64_t OC = w->ne[3] / IC;
|
int64_t OC = w->ne[3] / IC;
|
||||||
|
|||||||
@ -153,7 +153,8 @@ ggml_tensor* ggml_ext_conv_3d(ggml_context* ctx,
|
|||||||
int d0 = 1,
|
int d0 = 1,
|
||||||
int d1 = 1,
|
int d1 = 1,
|
||||||
int d2 = 1,
|
int d2 = 1,
|
||||||
bool force_prec_f32 = false);
|
bool force_prec_f32 = false,
|
||||||
|
bool direct = false);
|
||||||
|
|
||||||
// w: [OC,IC, KD, 1 * 1]
|
// w: [OC,IC, KD, 1 * 1]
|
||||||
// x: [N, IC, ID, IH*IW]
|
// x: [N, IC, ID, IH*IW]
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "core/util.h"
|
#include "core/util.h"
|
||||||
#include "ggml/src/ggml-impl.h"
|
#include "ggml-impl.h"
|
||||||
#include "stable-diffusion.h"
|
#include "stable-diffusion.h"
|
||||||
|
|
||||||
static std::string trim_copy(const std::string& value) {
|
static std::string trim_copy(const std::string& value) {
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
#include "ggml-alloc.h"
|
#include "ggml-alloc.h"
|
||||||
#include "ggml-backend.h"
|
#include "ggml-backend.h"
|
||||||
|
|
||||||
#include "ggml/src/ggml-impl.h"
|
#include "ggml-impl.h"
|
||||||
|
|
||||||
namespace sd::ggml_graph_cut {
|
namespace sd::ggml_graph_cut {
|
||||||
|
|
||||||
@ -426,8 +426,8 @@ namespace sd::ggml_graph_cut {
|
|||||||
if (tensor == nullptr || tensor->name[0] == '\0') {
|
if (tensor == nullptr || tensor->name[0] == '\0') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return starts_with(tensor->name, GGML_RUNNER_CUT_PREFIX) &&
|
return std::strncmp(tensor->name, GGML_RUNNER_CUT_PREFIX, std::strlen(GGML_RUNNER_CUT_PREFIX)) == 0 &&
|
||||||
ends_with(tensor->name, GGML_RUNNER_CUT_SUFFIX);
|
tensor->name[std::strlen(tensor->name) - 1] == GGML_RUNNER_CUT_SUFFIX[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string make_graph_cut_name(const std::string& group, const std::string& output) {
|
std::string make_graph_cut_name(const std::string& group, const std::string& output) {
|
||||||
@ -492,35 +492,88 @@ namespace sd::ggml_graph_cut {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint64_t> graph_layout(ggml_cgraph* graph, bool include_bindings) {
|
struct GraphLayoutTensors {
|
||||||
std::vector<const ggml_tensor*> tensors;
|
struct Entry {
|
||||||
std::unordered_map<const ggml_tensor*, size_t> indices;
|
const ggml_tensor* tensor = nullptr;
|
||||||
auto add = [&](const ggml_tensor* tensor) {
|
size_t index = 0;
|
||||||
if (tensor != nullptr && indices.emplace(tensor, tensors.size() + 1).second) {
|
|
||||||
tensors.push_back(tensor);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::vector<ggml_tensor*> tensors;
|
||||||
|
std::vector<Entry> entries;
|
||||||
|
|
||||||
|
explicit GraphLayoutTensors(size_t graph_size) {
|
||||||
|
tensors.reserve(graph_size);
|
||||||
|
size_t capacity = 2;
|
||||||
|
while (capacity < 2 * graph_size) {
|
||||||
|
capacity *= 2;
|
||||||
|
}
|
||||||
|
entries.resize(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t find(const ggml_tensor* tensor) const {
|
||||||
|
size_t hash = reinterpret_cast<uintptr_t>(tensor) >> 4;
|
||||||
|
hash ^= hash >> 16;
|
||||||
|
const size_t mask = entries.size() - 1;
|
||||||
|
size_t slot = hash & mask;
|
||||||
|
while (entries[slot].tensor != nullptr && entries[slot].tensor != tensor) {
|
||||||
|
slot = (slot + 1) & mask;
|
||||||
|
}
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(ggml_tensor* tensor) {
|
||||||
|
if (tensor == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
size_t slot = find(tensor);
|
||||||
|
if (entries[slot].tensor != nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (2 * (tensors.size() + 1) > entries.size()) {
|
||||||
|
// Segment graphs can reference tensors outside their node and leaf arrays.
|
||||||
|
std::vector<Entry> next(2 * entries.size());
|
||||||
|
entries.swap(next);
|
||||||
|
for (size_t i = 0; i < tensors.size(); ++i) {
|
||||||
|
entries[find(tensors[i])] = {tensors[i], i + 1};
|
||||||
|
}
|
||||||
|
slot = find(tensor);
|
||||||
|
}
|
||||||
|
entries[slot] = {tensor, tensors.size() + 1};
|
||||||
|
tensors.push_back(tensor);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index(const ggml_tensor* tensor) const {
|
||||||
|
return tensor == nullptr ? 0 : entries[find(tensor)].index;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<uint64_t> graph_layout(ggml_cgraph* graph, bool include_bindings) {
|
||||||
|
const size_t graph_size = static_cast<size_t>(graph->n_leafs) + graph->n_nodes;
|
||||||
|
GraphLayoutTensors layout_tensors(graph_size);
|
||||||
|
const auto& tensors = layout_tensors.tensors;
|
||||||
for (int i = 0; i < graph->n_leafs; ++i) {
|
for (int i = 0; i < graph->n_leafs; ++i) {
|
||||||
add(graph->leafs[i]);
|
layout_tensors.add(graph->leafs[i]);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < graph->n_nodes; ++i) {
|
for (int i = 0; i < graph->n_nodes; ++i) {
|
||||||
add(graph->nodes[i]);
|
layout_tensors.add(graph->nodes[i]);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < tensors.size(); ++i) {
|
for (size_t i = 0; i < tensors.size(); ++i) {
|
||||||
add(tensors[i]->view_src);
|
layout_tensors.add(tensors[i]->view_src);
|
||||||
for (auto source : tensors[i]->src) {
|
for (auto source : tensors[i]->src) {
|
||||||
add(source);
|
layout_tensors.add(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::vector<uint64_t> signature;
|
std::vector<uint64_t> signature;
|
||||||
signature.reserve(tensors.size() * 24);
|
const size_t tensor_fields = 5 + 2 * GGML_MAX_DIMS + GGML_MAX_SRC +
|
||||||
|
GGML_MAX_OP_PARAMS / sizeof(int32_t) + (include_bindings ? 2 : 0);
|
||||||
|
signature.reserve(2 + graph_size + tensors.size() * tensor_fields);
|
||||||
signature.push_back(graph->n_nodes);
|
signature.push_back(graph->n_nodes);
|
||||||
signature.push_back(graph->n_leafs);
|
signature.push_back(graph->n_leafs);
|
||||||
for (int i = 0; i < graph->n_leafs; ++i) {
|
for (int i = 0; i < graph->n_leafs; ++i) {
|
||||||
signature.push_back(indices.at(graph->leafs[i]));
|
signature.push_back(layout_tensors.index(graph->leafs[i]));
|
||||||
}
|
}
|
||||||
for (int i = 0; i < graph->n_nodes; ++i) {
|
for (int i = 0; i < graph->n_nodes; ++i) {
|
||||||
signature.push_back(indices.at(graph->nodes[i]));
|
signature.push_back(layout_tensors.index(graph->nodes[i]));
|
||||||
}
|
}
|
||||||
for (auto tensor : tensors) {
|
for (auto tensor : tensors) {
|
||||||
signature.push_back(tensor->op);
|
signature.push_back(tensor->op);
|
||||||
@ -536,9 +589,9 @@ namespace sd::ggml_graph_cut {
|
|||||||
signature.push_back(tensor->ne[d]);
|
signature.push_back(tensor->ne[d]);
|
||||||
signature.push_back(tensor->nb[d]);
|
signature.push_back(tensor->nb[d]);
|
||||||
}
|
}
|
||||||
signature.push_back(tensor->view_src == nullptr ? 0 : indices.at(tensor->view_src));
|
signature.push_back(layout_tensors.index(tensor->view_src));
|
||||||
for (auto source : tensor->src) {
|
for (auto source : tensor->src) {
|
||||||
signature.push_back(source == nullptr ? 0 : indices.at(source));
|
signature.push_back(layout_tensors.index(source));
|
||||||
}
|
}
|
||||||
if (!can_ignore_op_params(tensor->op)) {
|
if (!can_ignore_op_params(tensor->op)) {
|
||||||
for (int value : tensor->op_params) {
|
for (int value : tensor->op_params) {
|
||||||
@ -562,14 +615,18 @@ namespace sd::ggml_graph_cut {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::vector<std::pair<int, std::string>> cut_markers;
|
size_t cut_index = 0;
|
||||||
for (int i = 0; i < ggml_graph_n_nodes(gf); ++i) {
|
for (int i = 0; i < gf->n_nodes; ++i) {
|
||||||
auto node = ggml_graph_node(gf, i);
|
auto node = gf->nodes[i];
|
||||||
if (is_graph_cut_tensor(node)) {
|
if (is_graph_cut_tensor(node)) {
|
||||||
cut_markers.emplace_back(i, node->name);
|
if (cut_index >= plan.cut_markers.size() ||
|
||||||
|
plan.cut_markers[cut_index].first != i || plan.cut_markers[cut_index].second != node->name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
++cut_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cut_markers == plan.cut_markers;
|
return cut_index == plan.cut_markers.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool plan_matches_graph(ggml_cgraph* gf, const Plan& plan) {
|
bool plan_matches_graph(ggml_cgraph* gf, const Plan& plan) {
|
||||||
@ -948,11 +1005,11 @@ namespace sd::ggml_graph_cut {
|
|||||||
return plan;
|
return plan;
|
||||||
}
|
}
|
||||||
|
|
||||||
Plan resolve_plan(ggml_backend_t backend,
|
const Plan& resolve_plan(ggml_backend_t backend,
|
||||||
ggml_cgraph* gf,
|
ggml_cgraph* gf,
|
||||||
PlanCache* cache,
|
PlanCache* cache,
|
||||||
const std::unordered_set<const ggml_tensor*>& params_tensor_set,
|
const std::unordered_set<const ggml_tensor*>& params_tensor_set,
|
||||||
const char* log_desc) {
|
const char* log_desc) {
|
||||||
GGML_ASSERT(backend != nullptr);
|
GGML_ASSERT(backend != nullptr);
|
||||||
GGML_ASSERT(gf != nullptr);
|
GGML_ASSERT(gf != nullptr);
|
||||||
GGML_ASSERT(cache != nullptr);
|
GGML_ASSERT(cache != nullptr);
|
||||||
|
|||||||
@ -94,11 +94,12 @@ namespace sd::ggml_graph_cut {
|
|||||||
ggml_cgraph* gf,
|
ggml_cgraph* gf,
|
||||||
const std::unordered_set<const ggml_tensor*>& params_tensor_set,
|
const std::unordered_set<const ggml_tensor*>& params_tensor_set,
|
||||||
const char* log_desc);
|
const char* log_desc);
|
||||||
Plan resolve_plan(ggml_backend_t backend,
|
// The returned reference is valid until its cache entry is evicted or the cache is destroyed.
|
||||||
ggml_cgraph* gf,
|
const Plan& resolve_plan(ggml_backend_t backend,
|
||||||
PlanCache* cache,
|
ggml_cgraph* gf,
|
||||||
const std::unordered_set<const ggml_tensor*>& params_tensor_set,
|
PlanCache* cache,
|
||||||
const char* log_desc);
|
const std::unordered_set<const ggml_tensor*>& params_tensor_set,
|
||||||
|
const char* log_desc);
|
||||||
|
|
||||||
} // namespace sd::ggml_graph_cut
|
} // namespace sd::ggml_graph_cut
|
||||||
|
|
||||||
|
|||||||
@ -342,21 +342,17 @@ void GGMLRunner::copy_data_to_backend_tensor(ggml_cgraph* gf, bool clear_after_c
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GGMLRunner::resolve_graph_cut_plan(ggml_cgraph* gf,
|
const GGMLRunner::GraphCutPlan& GGMLRunner::resolve_graph_cut_plan(ggml_cgraph* gf) {
|
||||||
GraphCutPlan* plan_out) {
|
|
||||||
GGML_ASSERT(plan_out != nullptr);
|
|
||||||
GGML_ASSERT(gf != nullptr);
|
GGML_ASSERT(gf != nullptr);
|
||||||
*plan_out = sd::ggml_graph_cut::resolve_plan(runtime_backend,
|
return sd::ggml_graph_cut::resolve_plan(runtime_backend,
|
||||||
gf,
|
gf,
|
||||||
&graph_cut_plan_cache_,
|
&graph_cut_plan_cache_,
|
||||||
params_tensor_set_,
|
params_tensor_set_,
|
||||||
get_desc().c_str());
|
get_desc().c_str());
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GGMLRunner::resolve_graph_cut_layer_split_plan(ggml_cgraph* gf,
|
const GGMLRunner::GraphCutPlan& GGMLRunner::resolve_graph_cut_layer_split_plan(ggml_cgraph* gf) {
|
||||||
GraphCutPlan* plan_out) {
|
return resolve_graph_cut_plan(gf);
|
||||||
return resolve_graph_cut_plan(gf, plan_out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GGMLRunner::assign_graph_cut_layer_split_backends(ggml_cgraph* gf) {
|
bool GGMLRunner::assign_graph_cut_layer_split_backends(ggml_cgraph* gf) {
|
||||||
@ -369,10 +365,7 @@ bool GGMLRunner::assign_graph_cut_layer_split_backends(ggml_cgraph* gf) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphCutPlan plan;
|
const auto& plan = resolve_graph_cut_layer_split_plan(gf);
|
||||||
if (!resolve_graph_cut_layer_split_plan(gf, &plan)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!plan.valid || !plan.has_cuts || plan.segments.size() <= 1) {
|
if (!plan.valid || !plan.has_cuts || plan.segments.size() <= 1) {
|
||||||
auto manager = residency_manager.lock();
|
auto manager = residency_manager.lock();
|
||||||
if (manager == nullptr) {
|
if (manager == nullptr) {
|
||||||
@ -530,6 +523,7 @@ GGMLRunnerContext GGMLRunner::get_context() {
|
|||||||
runner_ctx.linear_scale = linear_scale;
|
runner_ctx.linear_scale = linear_scale;
|
||||||
runner_ctx.attn_scale = attn_scale;
|
runner_ctx.attn_scale = attn_scale;
|
||||||
runner_ctx.conv2d_direct_enabled = conv2d_direct_enabled;
|
runner_ctx.conv2d_direct_enabled = conv2d_direct_enabled;
|
||||||
|
runner_ctx.conv3d_direct_enabled = conv3d_direct_enabled;
|
||||||
runner_ctx.circular_x_enabled = circular_x_enabled;
|
runner_ctx.circular_x_enabled = circular_x_enabled;
|
||||||
runner_ctx.circular_y_enabled = circular_y_enabled;
|
runner_ctx.circular_y_enabled = circular_y_enabled;
|
||||||
runner_ctx.weight_adapter = weight_adapter;
|
runner_ctx.weight_adapter = weight_adapter;
|
||||||
@ -819,24 +813,25 @@ std::optional<Tensor<float>> GGMLRunner::execute_graph(ggml_cgraph* graph, int n
|
|||||||
if (!assign_graph_cut_layer_split_backends(graph)) {
|
if (!assign_graph_cut_layer_split_backends(graph)) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
const auto params = collect_used_param_tensors(graph);
|
const auto params = collect_used_param_tensors(graph);
|
||||||
ggml_graph_cut::Plan plan;
|
const auto& cached_plan = resolve_graph_cut_plan(graph);
|
||||||
if (!resolve_graph_cut_plan(graph, &plan)) {
|
const auto full_measurement = measure(graph, cached_plan.compute_buffer_size);
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
const auto full_measurement = measure(graph, plan.compute_buffer_size);
|
|
||||||
if (full_measurement.buffers.empty()) {
|
if (full_measurement.buffers.empty()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
auto manager = residency_manager.lock();
|
auto manager = residency_manager.lock();
|
||||||
const bool segmented = !is_multi_device() && !sd_backend_is_cpu(runtime_backend) &&
|
const bool segmented = !is_multi_device() && !sd_backend_is_cpu(runtime_backend) &&
|
||||||
manager != nullptr && manager->segmented_compute_enabled() &&
|
manager != nullptr && manager->segmented_compute_enabled() &&
|
||||||
plan.valid && plan.has_cuts && plan.segments.size() > 1 &&
|
cached_plan.valid && cached_plan.has_cuts && cached_plan.segments.size() > 1 &&
|
||||||
!fits(memory_requests(full_measurement.buffers, cache_.pending_bytes(graph)), params);
|
!fits(memory_requests(full_measurement.buffers, cache_.pending_bytes(graph)), params);
|
||||||
|
ggml_graph_cut::Plan monolithic_plan;
|
||||||
if (!segmented) {
|
if (!segmented) {
|
||||||
ggml_graph_cut::Segment segment;
|
monolithic_plan.segments.emplace_back();
|
||||||
|
auto& segment = monolithic_plan.segments.back();
|
||||||
segment.group_name = "graph";
|
segment.group_name = "graph";
|
||||||
segment.compute_buffer_size = plan.compute_buffer_size;
|
segment.compute_buffer_size = cached_plan.compute_buffer_size;
|
||||||
|
segment.internal_node_indices.reserve(ggml_graph_n_nodes(graph));
|
||||||
|
segment.input_refs.reserve(ggml_graph_cut::leaf_count(graph));
|
||||||
for (int i = 0; i < ggml_graph_n_nodes(graph); ++i) {
|
for (int i = 0; i < ggml_graph_n_nodes(graph); ++i) {
|
||||||
segment.internal_node_indices.push_back(i);
|
segment.internal_node_indices.push_back(i);
|
||||||
}
|
}
|
||||||
@ -849,8 +844,8 @@ std::optional<Tensor<float>> GGMLRunner::execute_graph(ggml_cgraph* graph, int n
|
|||||||
: ggml_graph_cut::Segment::INPUT_EXTERNAL;
|
: ggml_graph_cut::Segment::INPUT_EXTERNAL;
|
||||||
segment.input_refs.push_back(input);
|
segment.input_refs.push_back(input);
|
||||||
}
|
}
|
||||||
plan.segments = {std::move(segment)};
|
|
||||||
}
|
}
|
||||||
|
const auto& plan = segmented ? cached_plan : monolithic_plan;
|
||||||
const bool segments_changed = plan.segments.size() != logged_segment_count_;
|
const bool segments_changed = plan.segments.size() != logged_segment_count_;
|
||||||
if (segments_changed && (segmented || logged_segment_count_ > 1)) {
|
if (segments_changed && (segmented || logged_segment_count_ > 1)) {
|
||||||
LOG_VERBOSE("%s using %zu segment%s", get_desc().c_str(),
|
LOG_VERBOSE("%s using %zu segment%s", get_desc().c_str(),
|
||||||
@ -909,7 +904,10 @@ std::optional<Tensor<float>> GGMLRunner::execute_graph(ggml_cgraph* graph, int n
|
|||||||
auto ensure_capacity = [&]() {
|
auto ensure_capacity = [&]() {
|
||||||
sync_runtime_residency();
|
sync_runtime_residency();
|
||||||
auto requests = memory_requests(measurement.buffers, new_cache_bytes);
|
auto requests = memory_requests(measurement.buffers, new_cache_bytes);
|
||||||
if (!fits(requests, weights.params(index)) && workspace_.release_excess(measurement)) {
|
if (fits(requests, weights.params(index))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (workspace_.release_excess(measurement)) {
|
||||||
sync_runtime_residency();
|
sync_runtime_residency();
|
||||||
requests = memory_requests(measurement.buffers, new_cache_bytes);
|
requests = memory_requests(measurement.buffers, new_cache_bytes);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,6 +71,7 @@ struct GGMLRunnerContext {
|
|||||||
float linear_scale = 0.f;
|
float linear_scale = 0.f;
|
||||||
float attn_scale = 0.f;
|
float attn_scale = 0.f;
|
||||||
bool conv2d_direct_enabled = false;
|
bool conv2d_direct_enabled = false;
|
||||||
|
bool conv3d_direct_enabled = false;
|
||||||
bool circular_x_enabled = false;
|
bool circular_x_enabled = false;
|
||||||
bool circular_y_enabled = false;
|
bool circular_y_enabled = false;
|
||||||
ggml_tensor* ip_context = nullptr;
|
ggml_tensor* ip_context = nullptr;
|
||||||
@ -178,6 +179,7 @@ protected:
|
|||||||
float linear_scale = 0.f;
|
float linear_scale = 0.f;
|
||||||
float attn_scale = 0.f;
|
float attn_scale = 0.f;
|
||||||
bool conv2d_direct_enabled = false;
|
bool conv2d_direct_enabled = false;
|
||||||
|
bool conv3d_direct_enabled = false;
|
||||||
bool circular_x_enabled = false;
|
bool circular_x_enabled = false;
|
||||||
bool circular_y_enabled = false;
|
bool circular_y_enabled = false;
|
||||||
|
|
||||||
@ -263,11 +265,9 @@ protected:
|
|||||||
|
|
||||||
void copy_data_to_backend_tensor(ggml_cgraph* gf, bool clear_after_copy = true);
|
void copy_data_to_backend_tensor(ggml_cgraph* gf, bool clear_after_copy = true);
|
||||||
|
|
||||||
bool resolve_graph_cut_plan(ggml_cgraph* gf,
|
const GraphCutPlan& resolve_graph_cut_plan(ggml_cgraph* gf);
|
||||||
GraphCutPlan* plan_out);
|
|
||||||
|
|
||||||
bool resolve_graph_cut_layer_split_plan(ggml_cgraph* gf,
|
const GraphCutPlan& resolve_graph_cut_layer_split_plan(ggml_cgraph* gf);
|
||||||
GraphCutPlan* plan_out);
|
|
||||||
|
|
||||||
bool assign_graph_cut_layer_split_backends(ggml_cgraph* gf);
|
bool assign_graph_cut_layer_split_backends(ggml_cgraph* gf);
|
||||||
|
|
||||||
@ -346,6 +346,10 @@ public:
|
|||||||
conv2d_direct_enabled = enabled;
|
conv2d_direct_enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_conv3d_direct_enabled(bool enabled) {
|
||||||
|
conv3d_direct_enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
void set_circular_axes(bool circular_x, bool circular_y) {
|
void set_circular_axes(bool circular_x, bool circular_y) {
|
||||||
circular_x_enabled = circular_x;
|
circular_x_enabled = circular_x;
|
||||||
circular_y_enabled = circular_y;
|
circular_y_enabled = circular_y;
|
||||||
|
|||||||
143
src/core/parallel.cpp
Normal file
143
src/core/parallel.cpp
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
#include "core/parallel.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <exception>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace sd {
|
||||||
|
|
||||||
|
namespace parallel_detail {
|
||||||
|
thread_local ParallelExecutor* executor = nullptr;
|
||||||
|
thread_local bool active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ParallelExecutor::Impl {
|
||||||
|
struct Worker {
|
||||||
|
std::condition_variable wake;
|
||||||
|
std::thread thread;
|
||||||
|
bool ready = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
ParallelExecutor* owner;
|
||||||
|
std::mutex invocation_mutex;
|
||||||
|
std::mutex mutex;
|
||||||
|
std::condition_variable finished;
|
||||||
|
std::vector<std::unique_ptr<Worker>> workers;
|
||||||
|
bool stopping = false;
|
||||||
|
int pending = 0;
|
||||||
|
int participants = 1;
|
||||||
|
int64_t begin = 0;
|
||||||
|
int64_t count = 0;
|
||||||
|
const std::function<void(int64_t, int64_t)>* task = nullptr;
|
||||||
|
std::exception_ptr error;
|
||||||
|
|
||||||
|
explicit Impl(ParallelExecutor* owner)
|
||||||
|
: owner(owner) {}
|
||||||
|
|
||||||
|
~Impl() {
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
stopping = true;
|
||||||
|
}
|
||||||
|
for (auto& worker : workers) {
|
||||||
|
worker->wake.notify_one();
|
||||||
|
}
|
||||||
|
for (auto& worker : workers) {
|
||||||
|
worker->thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute(int index) {
|
||||||
|
ParallelScope scope(owner);
|
||||||
|
parallel_detail::Region region;
|
||||||
|
const int64_t size = count / participants;
|
||||||
|
const int64_t extra = count % participants;
|
||||||
|
const int64_t first = begin + index * size + std::min<int64_t>(index, extra);
|
||||||
|
const int64_t last = first + size + (index < extra ? 1 : 0);
|
||||||
|
try {
|
||||||
|
(*task)(first, last);
|
||||||
|
} catch (...) {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
|
if (!error) {
|
||||||
|
error = std::current_exception();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void worker_loop(Worker* worker, int index) {
|
||||||
|
std::unique_lock<std::mutex> lock(mutex);
|
||||||
|
for (;;) {
|
||||||
|
worker->wake.wait(lock, [&] { return stopping || worker->ready; });
|
||||||
|
if (stopping) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
worker->ready = false;
|
||||||
|
lock.unlock();
|
||||||
|
execute(index);
|
||||||
|
lock.lock();
|
||||||
|
if (--pending == 0) {
|
||||||
|
finished.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void run(int64_t first, int64_t last, int n_tasks, const std::function<void(int64_t, int64_t)>& callback) {
|
||||||
|
std::lock_guard<std::mutex> invocation_lock(invocation_mutex);
|
||||||
|
std::unique_lock<std::mutex> lock(mutex);
|
||||||
|
while (static_cast<int>(workers.size()) < n_tasks - 1) {
|
||||||
|
workers.push_back(std::make_unique<Worker>());
|
||||||
|
auto* worker = workers.back().get();
|
||||||
|
const int index = static_cast<int>(workers.size());
|
||||||
|
try {
|
||||||
|
worker->thread = std::thread([this, worker, index] { worker_loop(worker, index); });
|
||||||
|
} catch (...) {
|
||||||
|
workers.pop_back();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
begin = first;
|
||||||
|
count = last - first;
|
||||||
|
participants = n_tasks;
|
||||||
|
pending = n_tasks - 1;
|
||||||
|
task = &callback;
|
||||||
|
error = nullptr;
|
||||||
|
for (int i = 0; i < pending; ++i) {
|
||||||
|
workers[i]->ready = true;
|
||||||
|
workers[i]->wake.notify_one();
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
execute(0);
|
||||||
|
lock.lock();
|
||||||
|
finished.wait(lock, [&] { return pending == 0; });
|
||||||
|
task = nullptr;
|
||||||
|
if (error) {
|
||||||
|
std::rethrow_exception(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ParallelExecutor::ParallelExecutor(int n_threads)
|
||||||
|
: n_threads_(std::max(1, n_threads)), impl_(std::make_unique<Impl>(this)) {}
|
||||||
|
|
||||||
|
ParallelExecutor::~ParallelExecutor() = default;
|
||||||
|
|
||||||
|
void ParallelExecutor::run(int64_t begin, int64_t end, int64_t grain_size, const std::function<void(int64_t, int64_t)>& task) {
|
||||||
|
if (begin < 0 || grain_size <= 0) {
|
||||||
|
throw std::invalid_argument("parallel_for requires begin >= 0 and grain_size > 0");
|
||||||
|
}
|
||||||
|
if (end <= begin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int n_tasks = static_cast<int>(std::min<int64_t>(n_threads_, (end - begin) / grain_size));
|
||||||
|
if (parallel_detail::active || n_tasks <= 1) {
|
||||||
|
parallel_detail::Region region;
|
||||||
|
task(begin, end);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
impl_->run(begin, end, n_tasks, task);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
77
src/core/parallel.h
Normal file
77
src/core/parallel.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#ifndef __SD_CORE_PARALLEL_H__
|
||||||
|
#define __SD_CORE_PARALLEL_H__
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace sd {
|
||||||
|
|
||||||
|
class ParallelExecutor {
|
||||||
|
struct Impl;
|
||||||
|
int n_threads_;
|
||||||
|
std::unique_ptr<Impl> impl_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ParallelExecutor(int n_threads);
|
||||||
|
~ParallelExecutor();
|
||||||
|
ParallelExecutor(const ParallelExecutor&) = delete;
|
||||||
|
ParallelExecutor& operator=(const ParallelExecutor&) = delete;
|
||||||
|
|
||||||
|
int num_threads() const { return n_threads_; }
|
||||||
|
void run(int64_t begin, int64_t end, int64_t grain_size, const std::function<void(int64_t, int64_t)>& task);
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace parallel_detail {
|
||||||
|
extern thread_local ParallelExecutor* executor;
|
||||||
|
extern thread_local bool active;
|
||||||
|
|
||||||
|
class Region {
|
||||||
|
bool previous_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Region()
|
||||||
|
: previous_(active) { active = true; }
|
||||||
|
~Region() { active = previous_; }
|
||||||
|
Region(const Region&) = delete;
|
||||||
|
Region& operator=(const Region&) = delete;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParallelScope {
|
||||||
|
ParallelExecutor* previous_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ParallelScope(ParallelExecutor* executor)
|
||||||
|
: previous_(parallel_detail::executor) {
|
||||||
|
parallel_detail::executor = executor;
|
||||||
|
}
|
||||||
|
~ParallelScope() { parallel_detail::executor = previous_; }
|
||||||
|
ParallelScope(const ParallelScope&) = delete;
|
||||||
|
ParallelScope& operator=(const ParallelScope&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Ranges are non-negative. The callback may run concurrently and must own its writes.
|
||||||
|
template <typename F>
|
||||||
|
inline void parallel_for(int64_t begin, int64_t end, int64_t grain_size, F&& task) {
|
||||||
|
if (begin < 0 || grain_size <= 0) {
|
||||||
|
throw std::invalid_argument("parallel_for requires begin >= 0 and grain_size > 0");
|
||||||
|
}
|
||||||
|
if (end <= begin) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto* executor = parallel_detail::executor;
|
||||||
|
if (parallel_detail::active || executor == nullptr || executor->num_threads() <= 1 ||
|
||||||
|
(end - begin) / grain_size < 2) {
|
||||||
|
parallel_detail::Region region;
|
||||||
|
task(begin, end);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
executor->run(begin, end, grain_size, std::forward<F>(task));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __SD_CORE_PARALLEL_H__
|
||||||
@ -1,6 +1,7 @@
|
|||||||
#ifndef __SD_CORE_RNG_PHILOX_HPP__
|
#ifndef __SD_CORE_RNG_PHILOX_HPP__
|
||||||
#define __SD_CORE_RNG_PHILOX_HPP__
|
#define __SD_CORE_RNG_PHILOX_HPP__
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -14,60 +15,35 @@ private:
|
|||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<uint32_t> philox_m = {0xD2511F53, 0xCD9E8D57};
|
using Counter = std::array<std::vector<uint32_t>, 4>;
|
||||||
std::vector<uint32_t> philox_w = {0x9E3779B9, 0xBB67AE85};
|
|
||||||
float two_pow32_inv = 2.3283064e-10f;
|
|
||||||
float two_pow32_inv_2pi = 2.3283064e-10f * 6.2831855f;
|
|
||||||
|
|
||||||
std::vector<std::vector<uint32_t>> uint32(const std::vector<uint64_t>& x) {
|
static constexpr uint32_t philox_m[2] = {0xD2511F53, 0xCD9E8D57};
|
||||||
uint32_t N = (uint32_t)x.size();
|
static constexpr uint32_t philox_w[2] = {0x9E3779B9, 0xBB67AE85};
|
||||||
std::vector<std::vector<uint32_t>> result(2, std::vector<uint32_t>(N));
|
float two_pow32_inv = 2.3283064e-10f;
|
||||||
|
float two_pow32_inv_2pi = 2.3283064e-10f * 6.2831855f;
|
||||||
for (uint32_t i = 0; i < N; ++i) {
|
|
||||||
result[0][i] = static_cast<uint32_t>(x[i] & 0xFFFFFFFF);
|
|
||||||
result[1][i] = static_cast<uint32_t>(x[i] >> 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A single round of the Philox 4x32 random number generator.
|
// A single round of the Philox 4x32 random number generator.
|
||||||
void philox4_round(std::vector<std::vector<uint32_t>>& counter,
|
void philox4_round(Counter& counter, uint32_t key0, uint32_t key1) {
|
||||||
const std::vector<std::vector<uint32_t>>& key) {
|
|
||||||
uint32_t N = (uint32_t)counter[0].size();
|
uint32_t N = (uint32_t)counter[0].size();
|
||||||
for (uint32_t i = 0; i < N; i++) {
|
for (uint32_t i = 0; i < N; i++) {
|
||||||
const uint64_t v1 = static_cast<uint64_t>(counter[0][i]) * static_cast<uint64_t>(philox_m[0]);
|
const uint64_t v1 = static_cast<uint64_t>(counter[0][i]) * static_cast<uint64_t>(philox_m[0]);
|
||||||
const uint64_t v2 = static_cast<uint64_t>(counter[2][i]) * static_cast<uint64_t>(philox_m[1]);
|
const uint64_t v2 = static_cast<uint64_t>(counter[2][i]) * static_cast<uint64_t>(philox_m[1]);
|
||||||
|
|
||||||
counter[0][i] = static_cast<uint32_t>(v2 >> 32) ^ counter[1][i] ^ key[0][i];
|
counter[0][i] = static_cast<uint32_t>(v2 >> 32) ^ counter[1][i] ^ key0;
|
||||||
counter[1][i] = static_cast<uint32_t>(v2);
|
counter[1][i] = static_cast<uint32_t>(v2);
|
||||||
counter[2][i] = static_cast<uint32_t>(v1 >> 32) ^ counter[3][i] ^ key[1][i];
|
counter[2][i] = static_cast<uint32_t>(v1 >> 32) ^ counter[3][i] ^ key1;
|
||||||
counter[3][i] = static_cast<uint32_t>(v1);
|
counter[3][i] = static_cast<uint32_t>(v1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates 32-bit random numbers using the Philox 4x32 random number generator.
|
void philox4_32(Counter& counter, uint32_t key0, uint32_t key1, int rounds = 10) {
|
||||||
// Parameters:
|
|
||||||
// counter : A 4xN array of 32-bit integers representing the counter values (offset into generation).
|
|
||||||
// key : A 2xN array of 32-bit integers representing the key values (seed).
|
|
||||||
// rounds : The number of rounds to perform.
|
|
||||||
// Returns:
|
|
||||||
// std::vector<std::vector<uint32_t>>: A 4xN array of 32-bit integers containing the generated random numbers.
|
|
||||||
std::vector<std::vector<uint32_t>> philox4_32(std::vector<std::vector<uint32_t>>& counter,
|
|
||||||
std::vector<std::vector<uint32_t>>& key,
|
|
||||||
int rounds = 10) {
|
|
||||||
uint32_t N = (uint32_t)counter[0].size();
|
|
||||||
for (int i = 0; i < rounds - 1; ++i) {
|
for (int i = 0; i < rounds - 1; ++i) {
|
||||||
philox4_round(counter, key);
|
philox4_round(counter, key0, key1);
|
||||||
|
key0 += philox_w[0];
|
||||||
for (uint32_t j = 0; j < N; ++j) {
|
key1 += philox_w[1];
|
||||||
key[0][j] += philox_w[0];
|
|
||||||
key[1][j] += philox_w[1];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
philox4_round(counter, key);
|
philox4_round(counter, key0, key1);
|
||||||
return counter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float box_muller(float x, float y) {
|
float box_muller(float x, float y) {
|
||||||
@ -96,24 +72,22 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> randn(uint32_t n) override {
|
std::vector<float> randn(uint32_t n) override {
|
||||||
std::vector<std::vector<uint32_t>> counter(4, std::vector<uint32_t>(n, 0));
|
Counter counter;
|
||||||
for (uint32_t i = 0; i < n; i++) {
|
counter[0].resize(n, this->offset);
|
||||||
counter[0][i] = this->offset;
|
counter[1].resize(n);
|
||||||
}
|
counter[2].resize(n);
|
||||||
|
counter[3].resize(n);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < n; i++) {
|
for (uint32_t i = 0; i < n; i++) {
|
||||||
counter[2][i] = i;
|
counter[2][i] = i;
|
||||||
}
|
}
|
||||||
this->offset += 1;
|
this->offset += 1;
|
||||||
|
|
||||||
std::vector<uint64_t> key(n, this->seed);
|
philox4_32(counter, static_cast<uint32_t>(this->seed), static_cast<uint32_t>(this->seed >> 32));
|
||||||
std::vector<std::vector<uint32_t>> key_uint32 = uint32(key);
|
|
||||||
|
|
||||||
std::vector<std::vector<uint32_t>> g = philox4_32(counter, key_uint32);
|
std::vector<float> result(n);
|
||||||
|
|
||||||
std::vector<float> result;
|
|
||||||
for (uint32_t i = 0; i < n; ++i) {
|
for (uint32_t i = 0; i < n; ++i) {
|
||||||
result.push_back(box_muller((float)g[0][i], (float)g[1][i]));
|
result[i] = box_muller((float)counter[0][i], (float)counter[1][i]);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "core/parallel.h"
|
||||||
#include "core/rng.hpp"
|
#include "core/rng.hpp"
|
||||||
|
|
||||||
namespace sd {
|
namespace sd {
|
||||||
@ -59,6 +60,15 @@ namespace sd {
|
|||||||
return numel;
|
return numel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
inline void tensor_for_each(int64_t count, F&& fn, int64_t grain_size = 65536) {
|
||||||
|
parallel_for(0, count, grain_size, [&](int64_t begin, int64_t end) {
|
||||||
|
for (int64_t i = begin; i < end; ++i) {
|
||||||
|
fn(i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class Tensor {
|
class Tensor {
|
||||||
public:
|
public:
|
||||||
@ -230,7 +240,10 @@ namespace sd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fill_(const T& value) {
|
void fill_(const T& value) {
|
||||||
std::fill(data_.begin(), data_.end(), value);
|
const T fill_value = value;
|
||||||
|
parallel_for(0, numel(), 65536, [&](int64_t begin, int64_t end) {
|
||||||
|
std::fill_n(data_.data() + begin, end - begin, fill_value);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Tensor& masked_fill_(const Tensor<uint8_t>& mask, const T& value);
|
Tensor& masked_fill_(const Tensor<uint8_t>& mask, const T& value);
|
||||||
@ -390,7 +403,7 @@ namespace sd {
|
|||||||
tensor_shape_to_string(lhs) + ", rhs_shape=" +
|
tensor_shape_to_string(lhs) + ", rhs_shape=" +
|
||||||
tensor_shape_to_string(rhs));
|
tensor_shape_to_string(rhs));
|
||||||
}
|
}
|
||||||
shape[i] = std::max(lhs_dim, rhs_dim);
|
shape[i] = lhs_dim == 1 ? rhs_dim : lhs_dim;
|
||||||
}
|
}
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
@ -425,39 +438,55 @@ namespace sd {
|
|||||||
const std::vector<int64_t>& rhs_shape_raw,
|
const std::vector<int64_t>& rhs_shape_raw,
|
||||||
const std::vector<int64_t>& rhs_strides_raw,
|
const std::vector<int64_t>& rhs_strides_raw,
|
||||||
F&& fn) {
|
F&& fn) {
|
||||||
const size_t ndim = out_shape.size();
|
const int64_t numel = tensor_numel(out_shape);
|
||||||
std::vector<int64_t> out_strides = tensor_compute_strides(out_shape);
|
const size_t ndim = out_shape.size();
|
||||||
std::vector<int64_t> lhs_shape(ndim, 1);
|
auto broadcast_strides = [&](const std::vector<int64_t>& shape,
|
||||||
std::vector<int64_t> lhs_strides(ndim, 0);
|
const std::vector<int64_t>& strides) {
|
||||||
std::vector<int64_t> rhs_shape(ndim, 1);
|
if ((numel != 0 && tensor_numel(shape) == 0) || strides.size() != shape.size()) {
|
||||||
std::vector<int64_t> rhs_strides(ndim, 0);
|
tensor_throw_invalid_argument("Tensor broadcast requires non-empty inputs and matching strides");
|
||||||
|
}
|
||||||
for (size_t i = 0; i < lhs_shape_raw.size(); ++i) {
|
std::vector<int64_t> result(ndim, 0);
|
||||||
lhs_shape[i] = lhs_shape_raw[i];
|
for (size_t i = 0; i < std::max(ndim, shape.size()); ++i) {
|
||||||
lhs_strides[i] = lhs_strides_raw[i];
|
const int64_t input_dim = i < shape.size() ? shape[i] : 1;
|
||||||
}
|
const int64_t output_dim = i < ndim ? out_shape[i] : 1;
|
||||||
for (size_t i = 0; i < rhs_shape_raw.size(); ++i) {
|
if (input_dim != 1 && input_dim != output_dim) {
|
||||||
rhs_shape[i] = rhs_shape_raw[i];
|
tensor_throw_invalid_argument("Tensor broadcast cannot expand the destination: input_shape=" +
|
||||||
rhs_strides[i] = rhs_strides_raw[i];
|
tensor_shape_to_string(shape) + ", output_shape=" +
|
||||||
}
|
tensor_shape_to_string(out_shape));
|
||||||
|
|
||||||
const int64_t numel = tensor_numel(out_shape);
|
|
||||||
for (int64_t flat = 0; flat < numel; ++flat) {
|
|
||||||
int64_t remaining = flat;
|
|
||||||
int64_t lhs_offset = 0;
|
|
||||||
int64_t rhs_offset = 0;
|
|
||||||
for (size_t i = ndim; i-- > 0;) {
|
|
||||||
int64_t coord = remaining / out_strides[i];
|
|
||||||
remaining %= out_strides[i];
|
|
||||||
if (lhs_shape[i] != 1) {
|
|
||||||
lhs_offset += coord * lhs_strides[i];
|
|
||||||
}
|
}
|
||||||
if (rhs_shape[i] != 1) {
|
if (i < ndim && input_dim != 1) {
|
||||||
rhs_offset += coord * rhs_strides[i];
|
result[i] = strides[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn(flat, lhs_offset, rhs_offset);
|
return result;
|
||||||
|
};
|
||||||
|
const auto lhs_strides = broadcast_strides(lhs_shape_raw, lhs_strides_raw);
|
||||||
|
const auto rhs_strides = broadcast_strides(rhs_shape_raw, rhs_strides_raw);
|
||||||
|
if (numel == 0) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
parallel_for(0, numel, 16384, [&](int64_t begin, int64_t end) {
|
||||||
|
auto coord = tensor_unravel_index(begin, out_shape);
|
||||||
|
int64_t lhs_offset = 0;
|
||||||
|
int64_t rhs_offset = 0;
|
||||||
|
for (size_t i = 0; i < ndim; ++i) {
|
||||||
|
lhs_offset += coord[i] * lhs_strides[i];
|
||||||
|
rhs_offset += coord[i] * rhs_strides[i];
|
||||||
|
}
|
||||||
|
for (int64_t flat = begin; flat < end; ++flat) {
|
||||||
|
fn(flat, lhs_offset, rhs_offset);
|
||||||
|
for (size_t i = 0; i < ndim; ++i) {
|
||||||
|
lhs_offset += lhs_strides[i];
|
||||||
|
rhs_offset += rhs_strides[i];
|
||||||
|
if (++coord[i] < out_shape[i]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
coord[i] = 0;
|
||||||
|
lhs_offset -= out_shape[i] * lhs_strides[i];
|
||||||
|
rhs_offset -= out_shape[i] * rhs_strides[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -469,6 +498,7 @@ namespace sd {
|
|||||||
const std::vector<int64_t> data_strides = tensor_compute_strides(shape_);
|
const std::vector<int64_t> data_strides = tensor_compute_strides(shape_);
|
||||||
const std::vector<int64_t> mask_strides = tensor_compute_strides(mask.shape());
|
const std::vector<int64_t> mask_strides = tensor_compute_strides(mask.shape());
|
||||||
const uint8_t* mask_data = mask.data();
|
const uint8_t* mask_data = mask.data();
|
||||||
|
const T fill_value = value;
|
||||||
tensor_for_each_broadcast_offset(shape_,
|
tensor_for_each_broadcast_offset(shape_,
|
||||||
shape_,
|
shape_,
|
||||||
data_strides,
|
data_strides,
|
||||||
@ -476,7 +506,7 @@ namespace sd {
|
|||||||
mask_strides,
|
mask_strides,
|
||||||
[&](int64_t, int64_t data_offset, int64_t mask_offset) {
|
[&](int64_t, int64_t data_offset, int64_t mask_offset) {
|
||||||
if (mask_data[mask_offset] != 0) {
|
if (mask_data[mask_offset] != 0) {
|
||||||
data_[static_cast<size_t>(data_offset)] = value;
|
data_[static_cast<size_t>(data_offset)] = fill_value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return *this;
|
return *this;
|
||||||
@ -486,9 +516,9 @@ namespace sd {
|
|||||||
inline Tensor<uint8_t> operator<(const Tensor<T>& lhs, Scalar rhs) {
|
inline Tensor<uint8_t> operator<(const Tensor<T>& lhs, Scalar rhs) {
|
||||||
Tensor<uint8_t> result(lhs.shape());
|
Tensor<uint8_t> result(lhs.shape());
|
||||||
const T value = static_cast<T>(rhs);
|
const T value = static_cast<T>(rhs);
|
||||||
for (int64_t i = 0; i < lhs.numel(); ++i) {
|
tensor_for_each(lhs.numel(), [&](int64_t i) {
|
||||||
result[i] = lhs[i] < value ? 1 : 0;
|
result.data()[i] = lhs.data()[i] < value ? 1 : 0;
|
||||||
}
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -496,9 +526,9 @@ namespace sd {
|
|||||||
inline Tensor<uint8_t> operator<(Scalar lhs, const Tensor<T>& rhs) {
|
inline Tensor<uint8_t> operator<(Scalar lhs, const Tensor<T>& rhs) {
|
||||||
Tensor<uint8_t> result(rhs.shape());
|
Tensor<uint8_t> result(rhs.shape());
|
||||||
const T value = static_cast<T>(lhs);
|
const T value = static_cast<T>(lhs);
|
||||||
for (int64_t i = 0; i < rhs.numel(); ++i) {
|
tensor_for_each(rhs.numel(), [&](int64_t i) {
|
||||||
result[i] = value < rhs[i] ? 1 : 0;
|
result.data()[i] = value < rhs.data()[i] ? 1 : 0;
|
||||||
}
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,7 +546,7 @@ namespace sd {
|
|||||||
rhs.shape(),
|
rhs.shape(),
|
||||||
rhs_strides,
|
rhs_strides,
|
||||||
[&](int64_t flat, int64_t lhs_offset, int64_t rhs_offset) {
|
[&](int64_t flat, int64_t lhs_offset, int64_t rhs_offset) {
|
||||||
result[flat] = lhs_data[lhs_offset] < rhs_data[rhs_offset] ? 1 : 0;
|
result.data()[flat] = lhs_data[lhs_offset] < rhs_data[rhs_offset] ? 1 : 0;
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -524,9 +554,9 @@ namespace sd {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
inline Tensor<T>& operator+=(Tensor<T>& lhs, const Tensor<T>& rhs) {
|
inline Tensor<T>& operator+=(Tensor<T>& lhs, const Tensor<T>& rhs) {
|
||||||
if (lhs.shape() == rhs.shape()) {
|
if (lhs.shape() == rhs.shape()) {
|
||||||
for (int64_t i = 0; i < lhs.numel(); ++i) {
|
tensor_for_each(lhs.numel(), [&](int64_t i) {
|
||||||
lhs[i] += rhs[i];
|
lhs.data()[i] += rhs.data()[i];
|
||||||
}
|
});
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
tensor_broadcast_shape(lhs.shape(), rhs.shape());
|
tensor_broadcast_shape(lhs.shape(), rhs.shape());
|
||||||
@ -539,7 +569,7 @@ namespace sd {
|
|||||||
rhs.shape(),
|
rhs.shape(),
|
||||||
rhs_strides,
|
rhs_strides,
|
||||||
[&](int64_t, int64_t lhs_offset, int64_t rhs_offset) {
|
[&](int64_t, int64_t lhs_offset, int64_t rhs_offset) {
|
||||||
lhs[static_cast<int64_t>(lhs_offset)] += rhs_data[rhs_offset];
|
lhs.data()[lhs_offset] += rhs_data[rhs_offset];
|
||||||
});
|
});
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
@ -547,18 +577,18 @@ namespace sd {
|
|||||||
template <typename T, typename Scalar, typename = std::enable_if_t<std::is_arithmetic<Scalar>::value>>
|
template <typename T, typename Scalar, typename = std::enable_if_t<std::is_arithmetic<Scalar>::value>>
|
||||||
inline Tensor<T>& operator+=(Tensor<T>& lhs, Scalar rhs) {
|
inline Tensor<T>& operator+=(Tensor<T>& lhs, Scalar rhs) {
|
||||||
const T value = static_cast<T>(rhs);
|
const T value = static_cast<T>(rhs);
|
||||||
for (int64_t i = 0; i < lhs.numel(); ++i) {
|
tensor_for_each(lhs.numel(), [&](int64_t i) {
|
||||||
lhs[i] += value;
|
lhs.data()[i] += value;
|
||||||
}
|
});
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline Tensor<T>& operator-=(Tensor<T>& lhs, const Tensor<T>& rhs) {
|
inline Tensor<T>& operator-=(Tensor<T>& lhs, const Tensor<T>& rhs) {
|
||||||
if (lhs.shape() == rhs.shape()) {
|
if (lhs.shape() == rhs.shape()) {
|
||||||
for (int64_t i = 0; i < lhs.numel(); ++i) {
|
tensor_for_each(lhs.numel(), [&](int64_t i) {
|
||||||
lhs[i] -= rhs[i];
|
lhs.data()[i] -= rhs.data()[i];
|
||||||
}
|
});
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
tensor_broadcast_shape(lhs.shape(), rhs.shape());
|
tensor_broadcast_shape(lhs.shape(), rhs.shape());
|
||||||
@ -571,7 +601,7 @@ namespace sd {
|
|||||||
rhs.shape(),
|
rhs.shape(),
|
||||||
rhs_strides,
|
rhs_strides,
|
||||||
[&](int64_t, int64_t lhs_offset, int64_t rhs_offset) {
|
[&](int64_t, int64_t lhs_offset, int64_t rhs_offset) {
|
||||||
lhs[static_cast<int64_t>(lhs_offset)] -= rhs_data[rhs_offset];
|
lhs.data()[lhs_offset] -= rhs_data[rhs_offset];
|
||||||
});
|
});
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
@ -579,18 +609,18 @@ namespace sd {
|
|||||||
template <typename T, typename Scalar, typename = std::enable_if_t<std::is_arithmetic<Scalar>::value>>
|
template <typename T, typename Scalar, typename = std::enable_if_t<std::is_arithmetic<Scalar>::value>>
|
||||||
inline Tensor<T>& operator-=(Tensor<T>& lhs, Scalar rhs) {
|
inline Tensor<T>& operator-=(Tensor<T>& lhs, Scalar rhs) {
|
||||||
const T value = static_cast<T>(rhs);
|
const T value = static_cast<T>(rhs);
|
||||||
for (int64_t i = 0; i < lhs.numel(); ++i) {
|
tensor_for_each(lhs.numel(), [&](int64_t i) {
|
||||||
lhs[i] -= value;
|
lhs.data()[i] -= value;
|
||||||
}
|
});
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline Tensor<T>& operator*=(Tensor<T>& lhs, const Tensor<T>& rhs) {
|
inline Tensor<T>& operator*=(Tensor<T>& lhs, const Tensor<T>& rhs) {
|
||||||
if (lhs.shape() == rhs.shape()) {
|
if (lhs.shape() == rhs.shape()) {
|
||||||
for (int64_t i = 0; i < lhs.numel(); ++i) {
|
tensor_for_each(lhs.numel(), [&](int64_t i) {
|
||||||
lhs[i] *= rhs[i];
|
lhs.data()[i] *= rhs.data()[i];
|
||||||
}
|
});
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
tensor_broadcast_shape(lhs.shape(), rhs.shape());
|
tensor_broadcast_shape(lhs.shape(), rhs.shape());
|
||||||
@ -603,7 +633,7 @@ namespace sd {
|
|||||||
rhs.shape(),
|
rhs.shape(),
|
||||||
rhs_strides,
|
rhs_strides,
|
||||||
[&](int64_t, int64_t lhs_offset, int64_t rhs_offset) {
|
[&](int64_t, int64_t lhs_offset, int64_t rhs_offset) {
|
||||||
lhs[static_cast<int64_t>(lhs_offset)] *= rhs_data[rhs_offset];
|
lhs.data()[lhs_offset] *= rhs_data[rhs_offset];
|
||||||
});
|
});
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
@ -611,18 +641,18 @@ namespace sd {
|
|||||||
template <typename T, typename Scalar, typename = std::enable_if_t<std::is_arithmetic<Scalar>::value>>
|
template <typename T, typename Scalar, typename = std::enable_if_t<std::is_arithmetic<Scalar>::value>>
|
||||||
inline Tensor<T>& operator*=(Tensor<T>& lhs, Scalar rhs) {
|
inline Tensor<T>& operator*=(Tensor<T>& lhs, Scalar rhs) {
|
||||||
const T value = static_cast<T>(rhs);
|
const T value = static_cast<T>(rhs);
|
||||||
for (int64_t i = 0; i < lhs.numel(); ++i) {
|
tensor_for_each(lhs.numel(), [&](int64_t i) {
|
||||||
lhs[i] *= value;
|
lhs.data()[i] *= value;
|
||||||
}
|
});
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline Tensor<T>& operator/=(Tensor<T>& lhs, const Tensor<T>& rhs) {
|
inline Tensor<T>& operator/=(Tensor<T>& lhs, const Tensor<T>& rhs) {
|
||||||
if (lhs.shape() == rhs.shape()) {
|
if (lhs.shape() == rhs.shape()) {
|
||||||
for (int64_t i = 0; i < lhs.numel(); ++i) {
|
tensor_for_each(lhs.numel(), [&](int64_t i) {
|
||||||
lhs[i] /= rhs[i];
|
lhs.data()[i] /= rhs.data()[i];
|
||||||
}
|
});
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
tensor_broadcast_shape(lhs.shape(), rhs.shape());
|
tensor_broadcast_shape(lhs.shape(), rhs.shape());
|
||||||
@ -635,7 +665,7 @@ namespace sd {
|
|||||||
rhs.shape(),
|
rhs.shape(),
|
||||||
rhs_strides,
|
rhs_strides,
|
||||||
[&](int64_t, int64_t lhs_offset, int64_t rhs_offset) {
|
[&](int64_t, int64_t lhs_offset, int64_t rhs_offset) {
|
||||||
lhs[static_cast<int64_t>(lhs_offset)] /= rhs_data[rhs_offset];
|
lhs.data()[lhs_offset] /= rhs_data[rhs_offset];
|
||||||
});
|
});
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
@ -643,9 +673,9 @@ namespace sd {
|
|||||||
template <typename T, typename Scalar, typename = std::enable_if_t<std::is_arithmetic<Scalar>::value>>
|
template <typename T, typename Scalar, typename = std::enable_if_t<std::is_arithmetic<Scalar>::value>>
|
||||||
inline Tensor<T>& operator/=(Tensor<T>& lhs, Scalar rhs) {
|
inline Tensor<T>& operator/=(Tensor<T>& lhs, Scalar rhs) {
|
||||||
const T value = static_cast<T>(rhs);
|
const T value = static_cast<T>(rhs);
|
||||||
for (int64_t i = 0; i < lhs.numel(); ++i) {
|
tensor_for_each(lhs.numel(), [&](int64_t i) {
|
||||||
lhs[i] /= value;
|
lhs.data()[i] /= value;
|
||||||
}
|
});
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -664,7 +694,7 @@ namespace sd {
|
|||||||
rhs.shape(),
|
rhs.shape(),
|
||||||
rhs_strides,
|
rhs_strides,
|
||||||
[&](int64_t flat, int64_t lhs_offset, int64_t rhs_offset) {
|
[&](int64_t flat, int64_t lhs_offset, int64_t rhs_offset) {
|
||||||
result[flat] = lhs_data[lhs_offset] + rhs_data[rhs_offset];
|
result.data()[flat] = lhs_data[lhs_offset] + rhs_data[rhs_offset];
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -699,7 +729,7 @@ namespace sd {
|
|||||||
rhs.shape(),
|
rhs.shape(),
|
||||||
rhs_strides,
|
rhs_strides,
|
||||||
[&](int64_t flat, int64_t lhs_offset, int64_t rhs_offset) {
|
[&](int64_t flat, int64_t lhs_offset, int64_t rhs_offset) {
|
||||||
result[flat] = lhs_data[lhs_offset] - rhs_data[rhs_offset];
|
result.data()[flat] = lhs_data[lhs_offset] - rhs_data[rhs_offset];
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -717,9 +747,9 @@ namespace sd {
|
|||||||
inline Tensor<T> operator-(Scalar lhs, const Tensor<T>& rhs) {
|
inline Tensor<T> operator-(Scalar lhs, const Tensor<T>& rhs) {
|
||||||
Tensor<T> result = rhs;
|
Tensor<T> result = rhs;
|
||||||
const T value = static_cast<T>(lhs);
|
const T value = static_cast<T>(lhs);
|
||||||
for (int64_t i = 0; i < result.numel(); ++i) {
|
tensor_for_each(result.numel(), [&](int64_t i) {
|
||||||
result[i] = value - result[i];
|
result.data()[i] = value - result.data()[i];
|
||||||
}
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -738,7 +768,7 @@ namespace sd {
|
|||||||
rhs.shape(),
|
rhs.shape(),
|
||||||
rhs_strides,
|
rhs_strides,
|
||||||
[&](int64_t flat, int64_t lhs_offset, int64_t rhs_offset) {
|
[&](int64_t flat, int64_t lhs_offset, int64_t rhs_offset) {
|
||||||
result[flat] = lhs_data[lhs_offset] * rhs_data[rhs_offset];
|
result.data()[flat] = lhs_data[lhs_offset] * rhs_data[rhs_offset];
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -773,7 +803,7 @@ namespace sd {
|
|||||||
rhs.shape(),
|
rhs.shape(),
|
||||||
rhs_strides,
|
rhs_strides,
|
||||||
[&](int64_t flat, int64_t lhs_offset, int64_t rhs_offset) {
|
[&](int64_t flat, int64_t lhs_offset, int64_t rhs_offset) {
|
||||||
result[flat] = lhs_data[lhs_offset] / rhs_data[rhs_offset];
|
result.data()[flat] = lhs_data[lhs_offset] / rhs_data[rhs_offset];
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -791,18 +821,18 @@ namespace sd {
|
|||||||
inline Tensor<T> operator/(Scalar lhs, const Tensor<T>& rhs) {
|
inline Tensor<T> operator/(Scalar lhs, const Tensor<T>& rhs) {
|
||||||
Tensor<T> result = rhs;
|
Tensor<T> result = rhs;
|
||||||
const T value = static_cast<T>(lhs);
|
const T value = static_cast<T>(lhs);
|
||||||
for (int64_t i = 0; i < result.numel(); ++i) {
|
tensor_for_each(result.numel(), [&](int64_t i) {
|
||||||
result[i] = value / result[i];
|
result.data()[i] = value / result.data()[i];
|
||||||
}
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline Tensor<T> operator-(const Tensor<T>& tensor) {
|
inline Tensor<T> operator-(const Tensor<T>& tensor) {
|
||||||
Tensor<T> result = tensor;
|
Tensor<T> result = tensor;
|
||||||
for (int64_t i = 0; i < result.numel(); ++i) {
|
tensor_for_each(result.numel(), [&](int64_t i) {
|
||||||
result[i] = -result[i];
|
result.data()[i] = -result.data()[i];
|
||||||
}
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1067,9 +1097,11 @@ namespace sd {
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
inline Tensor<T> exp(const Tensor<T>& input) {
|
inline Tensor<T> exp(const Tensor<T>& input) {
|
||||||
Tensor<T> output(input.shape());
|
Tensor<T> output(input.shape());
|
||||||
for (int64_t i = 0; i < input.numel(); ++i) {
|
tensor_for_each(
|
||||||
output[i] = static_cast<T>(std::exp(static_cast<double>(input[i])));
|
input.numel(), [&](int64_t i) {
|
||||||
}
|
output.data()[i] = static_cast<T>(std::exp(static_cast<double>(input.data()[i])));
|
||||||
|
},
|
||||||
|
4096);
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1079,18 +1111,18 @@ namespace sd {
|
|||||||
tensor_throw_invalid_argument("Tensor clamp requires min_value <= max_value");
|
tensor_throw_invalid_argument("Tensor clamp requires min_value <= max_value");
|
||||||
}
|
}
|
||||||
Tensor<T> output(input.shape());
|
Tensor<T> output(input.shape());
|
||||||
for (int64_t i = 0; i < input.numel(); ++i) {
|
tensor_for_each(input.numel(), [&](int64_t i) {
|
||||||
output[i] = std::clamp(input[i], min_value, max_value);
|
output.data()[i] = std::clamp(input.data()[i], min_value, max_value);
|
||||||
}
|
});
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline Tensor<T> round(const Tensor<T>& input) {
|
inline Tensor<T> round(const Tensor<T>& input) {
|
||||||
Tensor<T> output(input.shape());
|
Tensor<T> output(input.shape());
|
||||||
for (int64_t i = 0; i < input.numel(); ++i) {
|
tensor_for_each(input.numel(), [&](int64_t i) {
|
||||||
output[i] = static_cast<T>(std::round(static_cast<double>(input[i])));
|
output.data()[i] = static_cast<T>(std::round(static_cast<double>(input.data()[i])));
|
||||||
}
|
});
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -414,14 +414,43 @@ std::vector<std::string> split_string(const std::string& str, char delimiter) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ggml_type sd_type_to_ggml_type(sd_type_t sdtype) {
|
ggml_type sd_type_to_ggml_type(sd_type_t sdtype) {
|
||||||
|
if (sdtype == SD_TYPE_F8_E4M3 || sdtype == SD_TYPE_F8_E5M2) {
|
||||||
|
#ifndef SD_USE_UPSTREAM_GGML
|
||||||
|
return sdtype == SD_TYPE_F8_E4M3 ? GGML_TYPE_F8_E4M3 : GGML_TYPE_F8_E5M2;
|
||||||
|
#else
|
||||||
|
return GGML_TYPE_COUNT;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
const int type_value = static_cast<int>(sdtype);
|
const int type_value = static_cast<int>(sdtype);
|
||||||
if (type_value < std::min<int>(SD_TYPE_COUNT, GGML_TYPE_COUNT)) {
|
if (type_value >= 0 && type_value < std::min<int>(SD_TYPE_COUNT, GGML_TYPE_COUNT)) {
|
||||||
return static_cast<ggml_type>(type_value);
|
return static_cast<ggml_type>(type_value);
|
||||||
} else {
|
} else {
|
||||||
return GGML_TYPE_COUNT;
|
return GGML_TYPE_COUNT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool validate_tensor_types(sd_type_t type, const char* tensor_type_rules) {
|
||||||
|
if (type != SD_TYPE_COUNT && sd_type_to_ggml_type(type) == GGML_TYPE_COUNT) {
|
||||||
|
LOG_ERROR("weight type %s is not supported by this ggml build", sd_type_name(type));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#ifdef SD_USE_UPSTREAM_GGML
|
||||||
|
for (const auto& rule : split_string(SAFE_STR(tensor_type_rules), ',')) {
|
||||||
|
const auto pos = rule.find('=');
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
const auto name = rule.substr(pos + 1);
|
||||||
|
if (name == "f8_e4m3" || name == "f8_e5m2") {
|
||||||
|
LOG_ERROR("FP8 is not supported by this ggml build (tensor type rule '%s')", rule.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
GGML_UNUSED(tensor_type_rules);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
KeyValueArgs parse_key_value_args(const char* args, const char* context) {
|
KeyValueArgs parse_key_value_args(const char* args, const char* context) {
|
||||||
KeyValueArgs pairs;
|
KeyValueArgs pairs;
|
||||||
|
|
||||||
@ -755,6 +784,13 @@ sd::Tensor<float> clip_preprocess(const sd::Tensor<float>& image, int target_wid
|
|||||||
int64_t resized_width = static_cast<int64_t>(scale * static_cast<float>(image.shape()[0]));
|
int64_t resized_width = static_cast<int64_t>(scale * static_cast<float>(image.shape()[0]));
|
||||||
int64_t resized_height = static_cast<int64_t>(scale * static_cast<float>(image.shape()[1]));
|
int64_t resized_height = static_cast<int64_t>(scale * static_cast<float>(image.shape()[1]));
|
||||||
|
|
||||||
|
// The resized image must cover the crop window. Floating-point rounding can
|
||||||
|
// leave a side one pixel short of the crop target (e.g. 730 -> 735.999...
|
||||||
|
// -> 735 after truncation), so clamp to keep the center crop in bounds.
|
||||||
|
// Truncation is otherwise preserved to avoid changing existing results.
|
||||||
|
resized_width = std::max<int64_t>(resized_width, target_width);
|
||||||
|
resized_height = std::max<int64_t>(resized_height, target_height);
|
||||||
|
|
||||||
sd::Tensor<float> resized = sd::ops::interpolate(
|
sd::Tensor<float> resized = sd::ops::interpolate(
|
||||||
image,
|
image,
|
||||||
{resized_width, resized_height, image.shape()[2], image.shape()[3]});
|
{resized_width, resized_height, image.shape()[2], image.shape()[3]});
|
||||||
|
|||||||
@ -90,6 +90,7 @@ void log_printf(sd_log_level_t level, const char* file, int line, const char* fo
|
|||||||
void sd_ggml_log_callback(ggml_log_level level, const char* text, void*);
|
void sd_ggml_log_callback(ggml_log_level level, const char* text, void*);
|
||||||
|
|
||||||
ggml_type sd_type_to_ggml_type(sd_type_t sdtype);
|
ggml_type sd_type_to_ggml_type(sd_type_t sdtype);
|
||||||
|
bool validate_tensor_types(sd_type_t type, const char* tensor_type_rules);
|
||||||
|
|
||||||
std::string trim(const std::string& s);
|
std::string trim(const std::string& s);
|
||||||
|
|
||||||
|
|||||||
@ -208,6 +208,7 @@ public:
|
|||||||
ggml_tensor* w = params["weight"];
|
ggml_tensor* w = params["weight"];
|
||||||
const float scale = ctx->linear_scale > 0.f ? ctx->linear_scale : this->scale;
|
const float scale = ctx->linear_scale > 0.f ? ctx->linear_scale : this->scale;
|
||||||
ggml_tensor* weight_scale = has_weight_scale ? params["weight_scale"] : nullptr;
|
ggml_tensor* weight_scale = has_weight_scale ? params["weight_scale"] : nullptr;
|
||||||
|
#ifndef SD_USE_UPSTREAM_GGML
|
||||||
if (w->type == GGML_TYPE_F8_E4M3 || w->type == GGML_TYPE_F8_E5M2) {
|
if (w->type == GGML_TYPE_F8_E4M3 || w->type == GGML_TYPE_F8_E5M2) {
|
||||||
bool supports_fp8_matmul = false;
|
bool supports_fp8_matmul = false;
|
||||||
if (ctx->backend != nullptr) {
|
if (ctx->backend != nullptr) {
|
||||||
@ -221,6 +222,7 @@ public:
|
|||||||
w = ggml_cast(ctx->ggml_ctx, w, GGML_TYPE_BF16);
|
w = ggml_cast(ctx->ggml_ctx, w, GGML_TYPE_BF16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
ggml_tensor* b = nullptr;
|
ggml_tensor* b = nullptr;
|
||||||
if (bias) {
|
if (bias) {
|
||||||
b = params["bias"];
|
b = params["bias"];
|
||||||
@ -238,6 +240,7 @@ public:
|
|||||||
if (ctx->weight_adapter && b != nullptr) {
|
if (ctx->weight_adapter && b != nullptr) {
|
||||||
b = ctx->weight_adapter->patch_weight(ctx->ggml_ctx, ctx->backend, b, prefix + "bias");
|
b = ctx->weight_adapter->patch_weight(ctx->ggml_ctx, ctx->backend, b, prefix + "bias");
|
||||||
}
|
}
|
||||||
|
#ifndef SD_USE_UPSTREAM_GGML
|
||||||
if (int8_convrot && scale == 1.f) {
|
if (int8_convrot && scale == 1.f) {
|
||||||
const auto cache_key = std::make_pair(x, int8_convrot_group_size);
|
const auto cache_key = std::make_pair(x, int8_convrot_group_size);
|
||||||
auto cached = ctx->int8_convrot_cache.find(cache_key);
|
auto cached = ctx->int8_convrot_cache.find(cache_key);
|
||||||
@ -248,6 +251,7 @@ public:
|
|||||||
x = cached->second;
|
x = cached->second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
out = ggml_ext_linear_i8_tensorwise(ctx->ggml_ctx,
|
out = ggml_ext_linear_i8_tensorwise(ctx->ggml_ctx,
|
||||||
x,
|
x,
|
||||||
w,
|
w,
|
||||||
@ -728,7 +732,7 @@ public:
|
|||||||
std::get<2>(stride), std::get<1>(stride), std::get<0>(stride),
|
std::get<2>(stride), std::get<1>(stride), std::get<0>(stride),
|
||||||
std::get<2>(padding), std::get<1>(padding), std::get<0>(padding),
|
std::get<2>(padding), std::get<1>(padding), std::get<0>(padding),
|
||||||
std::get<2>(dilation), std::get<1>(dilation), std::get<0>(dilation),
|
std::get<2>(dilation), std::get<1>(dilation), std::get<0>(dilation),
|
||||||
force_prec_f32);
|
force_prec_f32, ctx->conv3d_direct_enabled);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -78,7 +78,8 @@ namespace WAN {
|
|||||||
return ggml_ext_conv_3d(ctx->ggml_ctx, ctx->backend, x, w, b, in_channels,
|
return ggml_ext_conv_3d(ctx->ggml_ctx, ctx->backend, x, w, b, in_channels,
|
||||||
std::get<2>(stride), std::get<1>(stride), std::get<0>(stride),
|
std::get<2>(stride), std::get<1>(stride), std::get<0>(stride),
|
||||||
0, 0, 0,
|
0, 0, 0,
|
||||||
std::get<2>(dilation), std::get<1>(dilation), std::get<0>(dilation));
|
std::get<2>(dilation), std::get<1>(dilation), std::get<0>(dilation),
|
||||||
|
false, ctx->conv3d_direct_enabled);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -57,6 +57,18 @@ bool read_gguf_file(const std::string& file_path,
|
|||||||
|
|
||||||
size_t data_offset = gguf_reader.data_offset();
|
size_t data_offset = gguf_reader.data_offset();
|
||||||
for (const auto& gguf_tensor_info : gguf_reader.tensors()) {
|
for (const auto& gguf_tensor_info : gguf_reader.tensors()) {
|
||||||
|
#ifdef SD_USE_UPSTREAM_GGML
|
||||||
|
if (static_cast<int>(gguf_tensor_info.type) == SD_TYPE_F8_E4M3 ||
|
||||||
|
static_cast<int>(gguf_tensor_info.type) == SD_TYPE_F8_E5M2) {
|
||||||
|
set_error(error, "FP8 is not supported by this ggml build (tensor '" + gguf_tensor_info.name + "')");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (static_cast<unsigned>(gguf_tensor_info.type) >= GGML_TYPE_COUNT ||
|
||||||
|
ggml_get_type_traits(gguf_tensor_info.type)->type_size == 0) {
|
||||||
|
set_error(error, "unsupported GGUF tensor type (tensor '" + gguf_tensor_info.name + "')");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
TensorStorage tensor_storage(
|
TensorStorage tensor_storage(
|
||||||
gguf_tensor_info.name,
|
gguf_tensor_info.name,
|
||||||
gguf_tensor_info.type,
|
gguf_tensor_info.type,
|
||||||
|
|||||||
@ -86,10 +86,12 @@ static ggml_type safetensors_dtype_to_ggml_type(const std::string& dtype) {
|
|||||||
ttype = GGML_TYPE_F32;
|
ttype = GGML_TYPE_F32;
|
||||||
} else if (dtype == "F64") {
|
} else if (dtype == "F64") {
|
||||||
ttype = GGML_TYPE_F32;
|
ttype = GGML_TYPE_F32;
|
||||||
|
#ifndef SD_USE_UPSTREAM_GGML
|
||||||
} else if (dtype == "F8_E4M3") {
|
} else if (dtype == "F8_E4M3") {
|
||||||
ttype = GGML_TYPE_F8_E4M3;
|
ttype = GGML_TYPE_F8_E4M3;
|
||||||
} else if (dtype == "F8_E5M2") {
|
} else if (dtype == "F8_E5M2") {
|
||||||
ttype = GGML_TYPE_F8_E5M2;
|
ttype = GGML_TYPE_F8_E5M2;
|
||||||
|
#endif
|
||||||
} else if (dtype == "I32") {
|
} else if (dtype == "I32") {
|
||||||
ttype = GGML_TYPE_I32;
|
ttype = GGML_TYPE_I32;
|
||||||
} else if (dtype == "I64") {
|
} else if (dtype == "I64") {
|
||||||
@ -230,6 +232,12 @@ bool read_safetensors_file(const std::string& file_path,
|
|||||||
if (!read_comfy_quant_config(file, file_path, name, data_start + begin, end - begin, config, error)) {
|
if (!read_comfy_quant_config(file, file_path, name, data_start + begin, end - begin, config, error)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#ifdef SD_USE_UPSTREAM_GGML
|
||||||
|
if (config.format == "int8_tensorwise") {
|
||||||
|
set_error(error, "INT8 tensorwise/convrot is not supported by this ggml build (tensor '" + name + "')");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
const std::string module_name = name.substr(0, name.size() - std::string(".comfy_quant").size());
|
const std::string module_name = name.substr(0, name.size() - std::string(".comfy_quant").size());
|
||||||
comfy_quant_configs.emplace(module_name, std::move(config));
|
comfy_quant_configs.emplace(module_name, std::move(config));
|
||||||
}
|
}
|
||||||
@ -279,6 +287,12 @@ bool read_safetensors_file(const std::string& file_path,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SD_USE_UPSTREAM_GGML
|
||||||
|
if (dtype == "F8_E4M3" || dtype == "F8_E5M2") {
|
||||||
|
set_error(error, "FP8 is not supported by this ggml build (tensor '" + name + "')");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
ggml_type type = safetensors_dtype_to_ggml_type(dtype);
|
ggml_type type = safetensors_dtype_to_ggml_type(dtype);
|
||||||
if (type == GGML_TYPE_COUNT) {
|
if (type == GGML_TYPE_COUNT) {
|
||||||
set_error(error, "unsupported dtype '" + dtype + "' (tensor '" + name + "')");
|
set_error(error, "unsupported dtype '" + dtype + "' (tensor '" + name + "')");
|
||||||
|
|||||||
@ -274,6 +274,7 @@ bool ModelManager::register_param_tensors(ModelComponent component,
|
|||||||
new_states.push_back(std::move(state));
|
new_states.push_back(std::move(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolved_tensor_states_.clear();
|
||||||
for (auto& state : new_states) {
|
for (auto& state : new_states) {
|
||||||
TensorState* registered_state = state.get();
|
TensorState* registered_state = state.get();
|
||||||
tensor_states_by_tensor_[registered_state->tensor] = registered_state;
|
tensor_states_by_tensor_[registered_state->tensor] = registered_state;
|
||||||
@ -369,6 +370,7 @@ bool ModelManager::unregister_tensor_states(const std::unordered_set<TensorState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolved_tensor_states_.clear();
|
||||||
for (auto it = tensor_states_by_tensor_.begin(); it != tensor_states_by_tensor_.end();) {
|
for (auto it = tensor_states_by_tensor_.begin(); it != tensor_states_by_tensor_.end();) {
|
||||||
if (target_states.count(it->second) > 0) {
|
if (target_states.count(it->second) > 0) {
|
||||||
it = tensor_states_by_tensor_.erase(it);
|
it = tensor_states_by_tensor_.erase(it);
|
||||||
@ -1199,22 +1201,52 @@ bool ModelManager::resolve_required_tensor_states(const std::vector<ggml_tensor*
|
|||||||
std::vector<TensorState*>& required_states,
|
std::vector<TensorState*>& required_states,
|
||||||
ggml_backend_t compute_backend) const {
|
ggml_backend_t compute_backend) const {
|
||||||
required_states.clear();
|
required_states.clear();
|
||||||
|
required_states.reserve(tensors.size());
|
||||||
|
auto append_states = [&](const std::vector<TensorState*>& states) {
|
||||||
|
for (TensorState* state : states) {
|
||||||
|
if (compute_backend == nullptr || state->compute_backend == nullptr ||
|
||||||
|
state->compute_backend == compute_backend) {
|
||||||
|
required_states.push_back(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (auto it = resolved_tensor_states_.begin(); it != resolved_tensor_states_.end(); ++it) {
|
||||||
|
if (it->tensors == tensors) {
|
||||||
|
append_states(it->states);
|
||||||
|
resolved_tensor_states_.splice(resolved_tensor_states_.begin(), resolved_tensor_states_, it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<TensorState*> states;
|
||||||
|
states.reserve(tensors.size());
|
||||||
std::unordered_set<TensorState*> seen;
|
std::unordered_set<TensorState*> seen;
|
||||||
|
seen.reserve(tensors.size());
|
||||||
|
bool cacheable = true;
|
||||||
for (ggml_tensor* tensor : tensors) {
|
for (ggml_tensor* tensor : tensors) {
|
||||||
if (tensor == nullptr) {
|
if (tensor == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto param = resolve_param_tensor(tensor);
|
auto found = tensor_states_by_tensor_.find(tensor);
|
||||||
auto found = tensor_states_by_tensor_.find(param);
|
// Unregistered views can be rebound without changing the parameter list.
|
||||||
|
cacheable &= found != tensor_states_by_tensor_.end();
|
||||||
|
for (auto view = tensor->view_src; found == tensor_states_by_tensor_.end() && view != nullptr; view = view->view_src) {
|
||||||
|
found = tensor_states_by_tensor_.find(view);
|
||||||
|
}
|
||||||
if (found == tensor_states_by_tensor_.end()) {
|
if (found == tensor_states_by_tensor_.end()) {
|
||||||
LOG_ERROR("model manager tensor '%s' is not registered", ggml_get_name(tensor));
|
LOG_ERROR("model manager tensor '%s' is not registered", ggml_get_name(tensor));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
TensorState* state = found->second;
|
TensorState* state = found->second;
|
||||||
if ((compute_backend == nullptr || state->compute_backend == nullptr ||
|
if (seen.insert(state).second) {
|
||||||
state->compute_backend == compute_backend) &&
|
states.push_back(state);
|
||||||
seen.insert(state).second) {
|
}
|
||||||
required_states.push_back(state);
|
}
|
||||||
|
append_states(states);
|
||||||
|
if (cacheable && !tensors.empty()) {
|
||||||
|
static constexpr size_t MAX_RESOLVED_LISTS = 4;
|
||||||
|
resolved_tensor_states_.push_front({tensors, std::move(states)});
|
||||||
|
if (resolved_tensor_states_.size() > MAX_RESOLVED_LISTS) {
|
||||||
|
resolved_tensor_states_.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -1274,8 +1306,7 @@ size_t ModelManager::compute_backend_alloc_size(const std::vector<TensorState*>&
|
|||||||
size_t total_size = 0;
|
size_t total_size = 0;
|
||||||
std::unordered_set<TensorState*> seen;
|
std::unordered_set<TensorState*> seen;
|
||||||
for (TensorState* state : states) {
|
for (TensorState* state : states) {
|
||||||
if (state == nullptr || state->tensor == nullptr || !seen.insert(state).second ||
|
if (state == nullptr || state->tensor == nullptr) {
|
||||||
should_ignore(*state) || is_optional_missing_tensor(state->name)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const bool compute_resident =
|
const bool compute_resident =
|
||||||
@ -1285,6 +1316,9 @@ size_t ModelManager::compute_backend_alloc_size(const std::vector<TensorState*>&
|
|||||||
if (missing_only && compute_resident) {
|
if (missing_only && compute_resident) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!seen.insert(state).second || should_ignore(*state) || is_optional_missing_tensor(state->name)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ggml_backend_buffer_type_t buffer_type = nullptr;
|
ggml_backend_buffer_type_t buffer_type = nullptr;
|
||||||
if (state->compute_backend == state->params_backend) {
|
if (state->compute_backend == state->params_backend) {
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
#define __MODEL_MANAGER_H__
|
#define __MODEL_MANAGER_H__
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
@ -84,9 +85,15 @@ private:
|
|||||||
size_t resident_bytes = 0;
|
size_t resident_bytes = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ResolvedTensorStates {
|
||||||
|
std::vector<ggml_tensor*> tensors;
|
||||||
|
std::vector<TensorState*> states;
|
||||||
|
};
|
||||||
|
|
||||||
ModelLoader model_loader_;
|
ModelLoader model_loader_;
|
||||||
std::vector<std::unique_ptr<TensorState>> tensor_states_;
|
std::vector<std::unique_ptr<TensorState>> tensor_states_;
|
||||||
std::map<const ggml_tensor*, TensorState*> tensor_states_by_tensor_;
|
std::map<const ggml_tensor*, TensorState*> tensor_states_by_tensor_;
|
||||||
|
mutable std::list<ResolvedTensorStates> resolved_tensor_states_;
|
||||||
std::vector<std::unique_ptr<ParamsStorageBlock>> params_storage_blocks_;
|
std::vector<std::unique_ptr<ParamsStorageBlock>> params_storage_blocks_;
|
||||||
std::vector<std::unique_ptr<ComputeStagingBlock>> compute_staging_blocks_;
|
std::vector<std::unique_ptr<ComputeStagingBlock>> compute_staging_blocks_;
|
||||||
std::map<ggml_backend_t, ggml_backend_buffer_type_t> split_buffer_types_;
|
std::map<ggml_backend_t, ggml_backend_buffer_type_t> split_buffer_types_;
|
||||||
|
|||||||
@ -857,14 +857,24 @@ bool StableDiffusionGGML::init_model_loader(ModelLoader& model_loader, ModelConf
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool StableDiffusionGGML::init(const sd_ctx_params_t* sd_ctx_params) {
|
bool StableDiffusionGGML::init(const sd_ctx_params_t* sd_ctx_params) {
|
||||||
|
#ifdef SD_USE_UPSTREAM_GGML
|
||||||
|
LOG_WARN(
|
||||||
|
"Using upstream GGML: FP8 and INT8 tensorwise/convrot are disabled. "
|
||||||
|
"Some operators may be unsupported and performance may be lower than with patched GGML.");
|
||||||
|
#endif
|
||||||
|
if (!validate_tensor_types(sd_ctx_params->wtype, sd_ctx_params->tensor_type_rules)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
for (float scale : {sd_ctx_params->linear_scale, sd_ctx_params->attn_scale}) {
|
for (float scale : {sd_ctx_params->linear_scale, sd_ctx_params->attn_scale}) {
|
||||||
if (!std::isfinite(scale) || scale < 0.f || (scale > 0.f && !std::isfinite(1.f / scale))) {
|
if (!std::isfinite(scale) || scale < 0.f || (scale > 0.f && !std::isfinite(1.f / scale))) {
|
||||||
LOG_ERROR("scale overrides must be finite positive values, or 0 to keep model defaults");
|
LOG_ERROR("scale overrides must be finite positive values, or 0 to keep model defaults");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto configuration = std::make_unique<ModelConfig>(*sd_ctx_params);
|
auto configuration = std::make_unique<ModelConfig>(*sd_ctx_params);
|
||||||
n_threads = sd_ctx_params->n_threads;
|
n_threads = sd_ctx_params->n_threads;
|
||||||
|
tensor_executor = std::make_unique<sd::ParallelExecutor>(n_threads > 0 ? n_threads : sd_get_num_physical_cores());
|
||||||
|
sd::ParallelScope tensor_scope(tensor_executor.get());
|
||||||
enable_mmap = sd_ctx_params->enable_mmap;
|
enable_mmap = sd_ctx_params->enable_mmap;
|
||||||
disable_prefetch = sd_ctx_params->disable_prefetch;
|
disable_prefetch = sd_ctx_params->disable_prefetch;
|
||||||
disable_segmented_compute = sd_ctx_params->disable_segmented_compute;
|
disable_segmented_compute = sd_ctx_params->disable_segmented_compute;
|
||||||
@ -2158,6 +2168,10 @@ sd::Tensor<float> StableDiffusionGGML::sample(const std::shared_ptr<DiffusionMod
|
|||||||
|
|
||||||
RunnerEndOnExit sample_control_runner_end{!control_image.empty() && control_net != nullptr ? control_net.get() : nullptr};
|
RunnerEndOnExit sample_control_runner_end{!control_image.empty() && control_net != nullptr ? control_net.get() : nullptr};
|
||||||
|
|
||||||
|
const bool apply_denoise_mask = !denoise_mask.empty() &&
|
||||||
|
std::any_of(denoise_mask.values().begin(), denoise_mask.values().end(),
|
||||||
|
[](float value) { return value != 1.f; });
|
||||||
|
|
||||||
std::vector<int> skip_layers(guidance.slg.layers, guidance.slg.layers + guidance.slg.layer_count);
|
std::vector<int> skip_layers(guidance.slg.layers, guidance.slg.layers + guidance.slg.layer_count);
|
||||||
float cfg_scale = guidance.txt_cfg;
|
float cfg_scale = guidance.txt_cfg;
|
||||||
float img_cfg_scale = guidance.img_cfg;
|
float img_cfg_scale = guidance.img_cfg;
|
||||||
@ -2287,13 +2301,13 @@ sd::Tensor<float> StableDiffusionGGML::sample(const std::shared_ptr<DiffusionMod
|
|||||||
hunyuan_timestep_r_tensor = sd::Tensor<float>::from_vector({sigmas[step + 1]});
|
hunyuan_timestep_r_tensor = sd::Tensor<float>::from_vector({sigmas[step + 1]});
|
||||||
}
|
}
|
||||||
sd::Tensor<float> noised_input = x * c_in;
|
sd::Tensor<float> noised_input = x * c_in;
|
||||||
if (!denoise_mask.empty() && (version == VERSION_WAN2_2_TI2V || sd_version_is_ltxav(version) || sd_version_is_lingbot_video(version))) {
|
if (apply_denoise_mask && (version == VERSION_WAN2_2_TI2V || sd_version_is_ltxav(version) || sd_version_is_lingbot_video(version))) {
|
||||||
noised_input = noised_input * denoise_mask + sampling_init_latent * (1.0f - denoise_mask);
|
noised_input = noised_input * denoise_mask + sampling_init_latent * (1.0f - denoise_mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cache_runtime.spectrum_enabled && cache_runtime.spectrum.should_predict()) {
|
if (cache_runtime.spectrum_enabled && cache_runtime.spectrum.should_predict()) {
|
||||||
cache_runtime.spectrum.predict(&denoised);
|
cache_runtime.spectrum.predict(&denoised);
|
||||||
if (!denoise_mask.empty()) {
|
if (apply_denoise_mask) {
|
||||||
denoised = denoised * denoise_mask + sampling_init_latent * (1.0f - denoise_mask);
|
denoised = denoised * denoise_mask + sampling_init_latent * (1.0f - denoise_mask);
|
||||||
}
|
}
|
||||||
if (preview_needed && sd_should_preview_denoised()) {
|
if (preview_needed && sd_should_preview_denoised()) {
|
||||||
@ -2516,7 +2530,7 @@ sd::Tensor<float> StableDiffusionGGML::sample(const std::shared_ptr<DiffusionMod
|
|||||||
if (cache_runtime.spectrum_enabled) {
|
if (cache_runtime.spectrum_enabled) {
|
||||||
cache_runtime.spectrum.update(denoised);
|
cache_runtime.spectrum.update(denoised);
|
||||||
}
|
}
|
||||||
if (!denoise_mask.empty()) {
|
if (apply_denoise_mask) {
|
||||||
denoised = denoised * denoise_mask + sampling_init_latent * (1.0f - denoise_mask);
|
denoised = denoised * denoise_mask + sampling_init_latent * (1.0f - denoise_mask);
|
||||||
}
|
}
|
||||||
if (preview_needed && sd_should_preview_denoised()) {
|
if (preview_needed && sd_should_preview_denoised()) {
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -56,8 +57,9 @@ public:
|
|||||||
std::shared_ptr<RNG> rng;
|
std::shared_ptr<RNG> rng;
|
||||||
std::shared_ptr<RNG> sampler_rng = nullptr;
|
std::shared_ptr<RNG> sampler_rng = nullptr;
|
||||||
int n_threads = -1;
|
int n_threads = -1;
|
||||||
float default_flow_shift = INFINITY;
|
std::unique_ptr<sd::ParallelExecutor> tensor_executor;
|
||||||
float active_flow_shift = INFINITY;
|
float default_flow_shift = INFINITY;
|
||||||
|
float active_flow_shift = INFINITY;
|
||||||
|
|
||||||
std::shared_ptr<Conditioner> cond_stage_model;
|
std::shared_ptr<Conditioner> cond_stage_model;
|
||||||
std::shared_ptr<FrozenCLIPVisionEmbedder> clip_vision; // for svd or wan2.1 i2v
|
std::shared_ptr<FrozenCLIPVisionEmbedder> clip_vision; // for svd or wan2.1 i2v
|
||||||
@ -206,6 +208,7 @@ public:
|
|||||||
StableDiffusionGGML& sd;
|
StableDiffusionGGML& sd;
|
||||||
std::unique_lock<std::recursive_mutex> lock;
|
std::unique_lock<std::recursive_mutex> lock;
|
||||||
bool acquired = false;
|
bool acquired = false;
|
||||||
|
std::optional<sd::ParallelScope> tensor_scope;
|
||||||
|
|
||||||
explicit ContextOperation(StableDiffusionGGML& sd)
|
explicit ContextOperation(StableDiffusionGGML& sd)
|
||||||
: sd(sd), lock(sd.execution_mutex, std::try_to_lock) {
|
: sd(sd), lock(sd.execution_mutex, std::try_to_lock) {
|
||||||
@ -215,6 +218,7 @@ public:
|
|||||||
}
|
}
|
||||||
sd.executing_ = true;
|
sd.executing_ = true;
|
||||||
acquired = true;
|
acquired = true;
|
||||||
|
tensor_scope.emplace(sd.tensor_executor.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
~ContextOperation() {
|
~ContextOperation() {
|
||||||
|
|||||||
@ -584,10 +584,12 @@ namespace sd::model_builders {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (sd_ctx_params->vae_conv_direct) {
|
if (sd_ctx_params->vae_conv_direct) {
|
||||||
LOG_INFO("Using Conv2d direct in the vae model");
|
LOG_INFO("Using Conv2d/Conv3d direct in the vae model");
|
||||||
result.vae->set_conv2d_direct_enabled(true);
|
result.vae->set_conv2d_direct_enabled(true);
|
||||||
|
result.vae->set_conv3d_direct_enabled(true);
|
||||||
if (result.preview) {
|
if (result.preview) {
|
||||||
result.preview->set_conv2d_direct_enabled(true);
|
result.preview->set_conv2d_direct_enabled(true);
|
||||||
|
result.preview->set_conv3d_direct_enabled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (result.vae) {
|
if (result.vae) {
|
||||||
|
|||||||
@ -26,13 +26,26 @@ static float get_cache_reuse_threshold(const sd_cache_params_t& params) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char* sd_type_name(enum sd_type_t type) {
|
const char* sd_type_name(enum sd_type_t type) {
|
||||||
if ((int)type < std::min<int>(SD_TYPE_COUNT, GGML_TYPE_COUNT)) {
|
if (type == SD_TYPE_F8_E4M3) {
|
||||||
return ggml_type_name((ggml_type)type);
|
return "f8_e4m3";
|
||||||
|
}
|
||||||
|
if (type == SD_TYPE_F8_E5M2) {
|
||||||
|
return "f8_e5m2";
|
||||||
|
}
|
||||||
|
const auto ggml_type = sd_type_to_ggml_type(type);
|
||||||
|
if (ggml_type != GGML_TYPE_COUNT) {
|
||||||
|
return ggml_type_name(ggml_type);
|
||||||
}
|
}
|
||||||
return NONE_STR;
|
return NONE_STR;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum sd_type_t str_to_sd_type(const char* str) {
|
enum sd_type_t str_to_sd_type(const char* str) {
|
||||||
|
if (!strcmp(str, "f8_e4m3")) {
|
||||||
|
return SD_TYPE_F8_E4M3;
|
||||||
|
}
|
||||||
|
if (!strcmp(str, "f8_e5m2")) {
|
||||||
|
return SD_TYPE_F8_E5M2;
|
||||||
|
}
|
||||||
for (int i = 0; i < std::min<int>(SD_TYPE_COUNT, GGML_TYPE_COUNT); i++) {
|
for (int i = 0; i < std::min<int>(SD_TYPE_COUNT, GGML_TYPE_COUNT); i++) {
|
||||||
auto trait = ggml_get_type_traits((ggml_type)i);
|
auto trait = ggml_get_type_traits((ggml_type)i);
|
||||||
if (!strcmp(str, trait->type_name)) {
|
if (!strcmp(str, trait->type_name)) {
|
||||||
|
|||||||
@ -14,6 +14,7 @@ UpscalerGGML::UpscalerGGML(int n_threads,
|
|||||||
std::string backend_spec,
|
std::string backend_spec,
|
||||||
std::string params_backend_spec)
|
std::string params_backend_spec)
|
||||||
: n_threads(n_threads),
|
: n_threads(n_threads),
|
||||||
|
tensor_executor(n_threads > 0 ? n_threads : sd_get_num_physical_cores()),
|
||||||
direct(direct),
|
direct(direct),
|
||||||
tile_size(tile_size),
|
tile_size(tile_size),
|
||||||
backend_spec(std::move(backend_spec)),
|
backend_spec(std::move(backend_spec)),
|
||||||
@ -35,6 +36,7 @@ void UpscalerGGML::set_max_graph_vram_bytes(size_t max_vram_bytes) {
|
|||||||
|
|
||||||
bool UpscalerGGML::load_from_file(const std::string& esrgan_path,
|
bool UpscalerGGML::load_from_file(const std::string& esrgan_path,
|
||||||
int n_threads) {
|
int n_threads) {
|
||||||
|
sd::ParallelScope tensor_scope(&tensor_executor);
|
||||||
ggml_log_set(sd_ggml_log_callback, nullptr);
|
ggml_log_set(sd_ggml_log_callback, nullptr);
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
@ -108,6 +110,7 @@ bool UpscalerGGML::load_from_file(const std::string& esrgan_path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
sd::Tensor<float> UpscalerGGML::upscale_tensor(const sd::Tensor<float>& input_tensor) {
|
sd::Tensor<float> UpscalerGGML::upscale_tensor(const sd::Tensor<float>& input_tensor) {
|
||||||
|
sd::ParallelScope tensor_scope(&tensor_executor);
|
||||||
sd::Tensor<float> upscaled;
|
sd::Tensor<float> upscaled;
|
||||||
const int scale = esrgan_upscaler->config.scale;
|
const int scale = esrgan_upscaler->config.scale;
|
||||||
if (tile_size <= 0 || (input_tensor.shape()[0] <= tile_size && input_tensor.shape()[1] <= tile_size)) {
|
if (tile_size <= 0 || (input_tensor.shape()[0] <= tile_size && input_tensor.shape()[1] <= tile_size)) {
|
||||||
@ -142,6 +145,7 @@ sd::Tensor<float> UpscalerGGML::upscale_tensor(const sd::Tensor<float>& input_te
|
|||||||
}
|
}
|
||||||
|
|
||||||
sd_image_t UpscalerGGML::upscale(sd_image_t input_image, uint32_t upscale_factor) {
|
sd_image_t UpscalerGGML::upscale(sd_image_t input_image, uint32_t upscale_factor) {
|
||||||
|
sd::ParallelScope tensor_scope(&tensor_executor);
|
||||||
// upscale_factor, unused for RealESRGAN_x4plus_anime_6B.pth
|
// upscale_factor, unused for RealESRGAN_x4plus_anime_6B.pth
|
||||||
sd_image_t upscaled_image = {0, 0, 0, nullptr};
|
sd_image_t upscaled_image = {0, 0, 0, nullptr};
|
||||||
const int scale = esrgan_upscaler->config.scale;
|
const int scale = esrgan_upscaler->config.scale;
|
||||||
|
|||||||
@ -17,6 +17,7 @@ struct UpscalerGGML {
|
|||||||
std::shared_ptr<ESRGAN> esrgan_upscaler;
|
std::shared_ptr<ESRGAN> esrgan_upscaler;
|
||||||
std::string esrgan_path;
|
std::string esrgan_path;
|
||||||
int n_threads;
|
int n_threads;
|
||||||
|
sd::ParallelExecutor tensor_executor;
|
||||||
bool direct = false;
|
bool direct = false;
|
||||||
int tile_size = 128;
|
int tile_size = 128;
|
||||||
size_t max_graph_vram_bytes = 0;
|
size_t max_graph_vram_bytes = 0;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user