#include "transcribe-backend.h"
#include "transcribe-log.h"
#include <cstdlib>
#include <cstring>
#include <stdexcept>
namespace transcribe {
const char * kind_name(BackendKind kind) {
switch (kind) {
case BackendKind::Cpu:
return "cpu";
case BackendKind::Metal:
return "metal";
case BackendKind::Vulkan:
return "vulkan";
case BackendKind::Cuda:
return "cuda";
case BackendKind::Rocm:
return "rocm";
case BackendKind::Sycl:
return "sycl";
case BackendKind::Accel:
return "accel";
case BackendKind::OtherGpu:
return "gpu";
case BackendKind::Unknown:
default:
return "unknown";
}
}
static bool reg_name_is(const char * reg_name, const char * prefix) {
if (reg_name == nullptr || prefix == nullptr) {
return false;
}
return std::strncmp(reg_name, prefix, std::strlen(prefix)) == 0;
}
BackendKind classify_backend_type(enum ggml_backend_dev_type dev_type, const char * reg_name) {
if (dev_type == GGML_BACKEND_DEVICE_TYPE_CPU) {
return BackendKind::Cpu;
}
if (dev_type == GGML_BACKEND_DEVICE_TYPE_ACCEL) {
return BackendKind::Accel;
}
if (dev_type != GGML_BACKEND_DEVICE_TYPE_GPU && dev_type != GGML_BACKEND_DEVICE_TYPE_IGPU) {
return BackendKind::Unknown;
}
if (reg_name_is(reg_name, "MTL") || reg_name_is(reg_name, "Metal")) {
return BackendKind::Metal;
} else if (reg_name_is(reg_name, "Vulkan")) {
return BackendKind::Vulkan;
} else if (reg_name_is(reg_name, "CUDA")) {
return BackendKind::Cuda;
} else if (reg_name_is(reg_name, "ROCm")) {
return BackendKind::Rocm;
} else if (reg_name_is(reg_name, "SYCL")) {
return BackendKind::Sycl;
}
return BackendKind::OtherGpu;
}
BackendKind classify_device(ggml_backend_dev_t dev) {
if (dev == nullptr) {
return BackendKind::Unknown;
}
const auto dev_type = ggml_backend_dev_type(dev);
ggml_backend_reg_t reg = ggml_backend_dev_backend_reg(dev);
const char * reg_name = (reg != nullptr) ? ggml_backend_reg_name(reg) : nullptr;
return classify_backend_type(dev_type, reg_name);
}
std::vector<size_t> gpu_probe_order(const std::vector<enum ggml_backend_dev_type> & dev_types) {
std::vector<size_t> order;
order.reserve(dev_types.size());
for (size_t i = 0; i < dev_types.size(); ++i) {
if (dev_types[i] == GGML_BACKEND_DEVICE_TYPE_GPU) {
order.push_back(i);
}
}
for (size_t i = 0; i < dev_types.size(); ++i) {
if (dev_types[i] == GGML_BACKEND_DEVICE_TYPE_IGPU) {
order.push_back(i);
}
}
return order;
}
namespace {
template <typename Fn> void contained_free(const char * what, Fn && do_free) noexcept {
try {
do_free();
if (const char * hook = std::getenv("TRANSCRIBE_TEST_TEARDOWN_THROW"); hook != nullptr && hook[0] != '\0') {
throw std::runtime_error("TRANSCRIBE_TEST_TEARDOWN_THROW fault injection");
}
} catch (const std::exception & e) {
log_msg(TRANSCRIBE_LOG_LEVEL_WARN, "%s threw during teardown (contained; resource may leak): %s", what,
e.what());
} catch (...) {
log_msg(TRANSCRIBE_LOG_LEVEL_WARN,
"%s threw an unknown exception during teardown (contained; resource may leak)", what);
}
}
}
void safe_backend_free(ggml_backend_t backend) noexcept {
if (backend == nullptr) {
return;
}
contained_free("ggml_backend_free", [&] { ggml_backend_free(backend); });
}
void safe_buffer_free(ggml_backend_buffer_t buffer) noexcept {
if (buffer == nullptr) {
return;
}
contained_free("ggml_backend_buffer_free", [&] { ggml_backend_buffer_free(buffer); });
}
void safe_sched_free(ggml_backend_sched_t sched) noexcept {
if (sched == nullptr) {
return;
}
contained_free("ggml_backend_sched_free", [&] { ggml_backend_sched_free(sched); });
}
}