482 lines
17 KiB
C++
482 lines
17 KiB
C++
#ifdef _WIN32
|
|
#ifndef NOMINMAX
|
|
#define NOMINMAX
|
|
#endif
|
|
#endif
|
|
|
|
#include "vitisai/whisper-vitisai-helpers.h"
|
|
|
|
#include <algorithm>
|
|
#include <cstdio>
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#endif
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
namespace whisper_vitisai_helpers {
|
|
|
|
bool map_rai_file(const char * path, uint8_t ** buffer, size_t * size) {
|
|
#ifdef _WIN32
|
|
HANDLE hFile = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
std::fprintf(stderr, "%s: %d: Failed to open rai file '%s'\n", __func__, __LINE__, path);
|
|
return false;
|
|
}
|
|
|
|
LARGE_INTEGER fileSize;
|
|
if (!GetFileSizeEx(hFile, &fileSize)) {
|
|
CloseHandle(hFile);
|
|
std::fprintf(stderr, "%s: %d: Failed to get file size for rai file '%s'\n", __func__, __LINE__, path);
|
|
return false;
|
|
}
|
|
|
|
HANDLE hMapping = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, fileSize.QuadPart, NULL);
|
|
if (hMapping == NULL) {
|
|
CloseHandle(hFile);
|
|
std::fprintf(stderr, "%s: %d: Failed to create file mapping for rai file '%s'\n", __func__, __LINE__, path);
|
|
return false;
|
|
}
|
|
|
|
*buffer = (uint8_t *) MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, fileSize.QuadPart);
|
|
if (*buffer == NULL) {
|
|
CloseHandle(hMapping);
|
|
CloseHandle(hFile);
|
|
std::fprintf(stderr, "%s: %d: Failed to map rai file '%s'\n", __func__, __LINE__, path);
|
|
return false;
|
|
}
|
|
CloseHandle(hMapping);
|
|
CloseHandle(hFile);
|
|
*size = fileSize.QuadPart;
|
|
return true;
|
|
#else
|
|
FILE * fd = fopen(path, "rb");
|
|
if (!fd) {
|
|
std::fprintf(stderr, "%s: %d: Failed to open rai file '%s'\n", __func__, __LINE__, path);
|
|
return false;
|
|
}
|
|
|
|
struct stat st;
|
|
if (fstat(fileno(fd), &st) == -1) {
|
|
fclose(fd);
|
|
std::fprintf(stderr, "%s: %d: Failed to get file size for rai file '%s'\n", __func__, __LINE__, path);
|
|
return false;
|
|
}
|
|
|
|
*buffer = (uint8_t *) mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fileno(fd), 0);
|
|
if (*buffer == MAP_FAILED) {
|
|
fclose(fd);
|
|
std::fprintf(stderr, "%s: %d: Failed to mmap rai file '%s'\n", __func__, __LINE__, path);
|
|
return false;
|
|
}
|
|
fclose(fd);
|
|
*size = st.st_size;
|
|
return true;
|
|
#endif // _WIN32
|
|
}
|
|
|
|
void unmap_rai_file(uint8_t * buffer, size_t size) {
|
|
#ifdef _WIN32
|
|
UnmapViewOfFile(buffer);
|
|
#else
|
|
munmap(buffer, size);
|
|
#endif // _WIN32
|
|
}
|
|
|
|
bool file_exists(const char * path) {
|
|
if (!path) {
|
|
return false;
|
|
}
|
|
|
|
FILE * file = fopen(path, "rb");
|
|
if (!file) {
|
|
return false;
|
|
}
|
|
fclose(file);
|
|
return true;
|
|
}
|
|
|
|
const char * whisper_kv_type_name(ggml_type type) {
|
|
switch (type) {
|
|
case GGML_TYPE_F32: return "F32";
|
|
case GGML_TYPE_F16: return "F16";
|
|
default: return "unsupported";
|
|
}
|
|
}
|
|
|
|
const char * whisper_flexml_dtype_name(flexmlrt::client::DataType type) {
|
|
switch (type) {
|
|
case flexmlrt::client::DataType::Float32: return "Float32";
|
|
case flexmlrt::client::DataType::Int8: return "Int8";
|
|
case flexmlrt::client::DataType::UInt8: return "UInt8";
|
|
case flexmlrt::client::DataType::Int16: return "Int16";
|
|
case flexmlrt::client::DataType::UInt16: return "UInt16";
|
|
case flexmlrt::client::DataType::BFloat16: return "BFloat16";
|
|
case flexmlrt::client::DataType::Bool: return "Bool";
|
|
case flexmlrt::client::DataType::Float16: return "Float16";
|
|
case flexmlrt::client::DataType::Int32: return "Int32";
|
|
case flexmlrt::client::DataType::UInt32: return "UInt32";
|
|
default: return "Unknown";
|
|
}
|
|
}
|
|
|
|
bool whisper_flexml_dtype_to_ggml_type(
|
|
flexmlrt::client::DataType type,
|
|
ggml_type * ggml_dtype) {
|
|
switch (type) {
|
|
case flexmlrt::client::DataType::Float32:
|
|
if (ggml_dtype) {
|
|
*ggml_dtype = GGML_TYPE_F32;
|
|
}
|
|
return true;
|
|
case flexmlrt::client::DataType::Float16:
|
|
if (ggml_dtype) {
|
|
*ggml_dtype = GGML_TYPE_F16;
|
|
}
|
|
return true;
|
|
case flexmlrt::client::DataType::BFloat16:
|
|
if (ggml_dtype) {
|
|
*ggml_dtype = GGML_TYPE_BF16;
|
|
}
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool whisper_vitisai_validate_tensor_dtype(
|
|
const char * tensor_name,
|
|
flexmlrt::client::DataType model_dtype,
|
|
ggml_type runtime_dtype) {
|
|
ggml_type expected_runtime_dtype = GGML_TYPE_COUNT;
|
|
if (!whisper_flexml_dtype_to_ggml_type(model_dtype, &expected_runtime_dtype)) {
|
|
std::fprintf(stderr,
|
|
"%s: unsupported model dtype for %s: %s (supported: Float32/Float16/BFloat16)\n",
|
|
__func__, tensor_name, whisper_flexml_dtype_name(model_dtype));
|
|
return false;
|
|
}
|
|
|
|
if (runtime_dtype != expected_runtime_dtype) {
|
|
std::fprintf(stderr,
|
|
"%s: %s dtype mismatch (runtime=%s, model=%s)\n",
|
|
__func__, tensor_name, ggml_type_name(runtime_dtype), whisper_flexml_dtype_name(model_dtype));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static std::string whisper_shape_to_string(const std::vector<size_t> & shape) {
|
|
std::string out = "[";
|
|
for (size_t i = 0; i < shape.size(); ++i) {
|
|
if (i > 0) {
|
|
out += ", ";
|
|
}
|
|
out += std::to_string(shape[i]);
|
|
}
|
|
out += "]";
|
|
return out;
|
|
}
|
|
|
|
static std::vector<size_t> whisper_canonical_shape(const std::vector<std::uint32_t> & shape) {
|
|
std::vector<size_t> canonical;
|
|
canonical.reserve(shape.size());
|
|
for (size_t i = 0; i < shape.size(); ++i) {
|
|
const size_t dim = (size_t) shape[i];
|
|
if (dim != 1) {
|
|
canonical.push_back(dim);
|
|
}
|
|
}
|
|
if (canonical.empty()) {
|
|
canonical.push_back(1);
|
|
}
|
|
return canonical;
|
|
}
|
|
|
|
static bool whisper_validate_shape(
|
|
const char * tensor_name,
|
|
const std::vector<std::uint32_t> & model_shape,
|
|
const std::vector<size_t> & expected_shape) {
|
|
const std::vector<size_t> shape = whisper_canonical_shape(model_shape);
|
|
if (shape != expected_shape) {
|
|
std::fprintf(stderr,
|
|
"%s: %s shape mismatch (runtime expected=%s, model=%s)\n",
|
|
__func__,
|
|
tensor_name,
|
|
whisper_shape_to_string(expected_shape).c_str(),
|
|
whisper_shape_to_string(shape).c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool whisper_validate_cross_shape(
|
|
const char * tensor_name,
|
|
const std::vector<std::uint32_t> & model_shape,
|
|
int n_text_layer,
|
|
int n_ctx,
|
|
int n_state) {
|
|
const std::vector<size_t> expected = {
|
|
(size_t) n_text_layer,
|
|
(size_t) n_ctx,
|
|
(size_t) n_state,
|
|
};
|
|
return whisper_validate_shape(tensor_name, model_shape, expected);
|
|
}
|
|
|
|
bool whisper_vitisai_bind_tensor_data(
|
|
const char * tensor_name,
|
|
struct ggml_tensor * runtime_tensor,
|
|
const std::vector<size_t> & expected_shape,
|
|
flexmlrt::client::ErtTensorType & io_tensor) {
|
|
const auto & meta = io_tensor.getMetadata();
|
|
if (!whisper_vitisai_validate_tensor_dtype(tensor_name, meta.type, runtime_tensor->type)) {
|
|
return false;
|
|
}
|
|
if (!whisper_validate_shape(tensor_name, meta.shape, expected_shape)) {
|
|
return false;
|
|
}
|
|
|
|
const size_t model_bytes = meta.size;
|
|
const size_t runtime_bytes = ggml_nbytes(runtime_tensor);
|
|
if (model_bytes == 0 || runtime_bytes == 0) {
|
|
std::fprintf(stderr, "%s: %s sizes must be non-zero (model=%zu, runtime=%zu)\n",
|
|
__func__, tensor_name, model_bytes, runtime_bytes);
|
|
return false;
|
|
}
|
|
if (runtime_bytes != model_bytes) {
|
|
std::fprintf(stderr,
|
|
"%s: %s tensor size mismatch (runtime=%zu B, model=%zu B). "
|
|
"VitisAI .rai requires exact context match; use matching -ac/model artifact.\n",
|
|
__func__, tensor_name, runtime_bytes, model_bytes);
|
|
return false;
|
|
}
|
|
|
|
io_tensor.data = runtime_tensor->data;
|
|
return true;
|
|
}
|
|
|
|
bool whisper_vitisai_resolve_io_binding(
|
|
const char * caller,
|
|
const std::vector<flexmlrt::client::ErtTensorType> & input_tensors,
|
|
const std::vector<flexmlrt::client::ErtTensorType> & output_tensors,
|
|
whisper_vitisai_io_binding * binding,
|
|
std::string * error) {
|
|
const auto fail = [error](std::string message) {
|
|
if (error) {
|
|
*error = std::move(message);
|
|
}
|
|
return false;
|
|
};
|
|
|
|
if (input_tensors.empty()) {
|
|
return fail("Model has no input tensors");
|
|
}
|
|
|
|
binding->mel_in_idx = 0;
|
|
bool found_named_mel = false;
|
|
for (int i = 0; i < (int) input_tensors.size(); ++i) {
|
|
const std::string & name = input_tensors[i].getMetadata().name;
|
|
if (name == "input" || name == "mel") {
|
|
binding->mel_in_idx = i;
|
|
found_named_mel = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!found_named_mel) {
|
|
std::fprintf(stderr, "%s: WARNING: mel input not found by name; falling back to input[0]\n", caller);
|
|
}
|
|
|
|
if (output_tensors.empty()) {
|
|
return fail("Model has no output tensors");
|
|
}
|
|
|
|
for (int i = 0; i < (int) output_tensors.size(); ++i) {
|
|
const std::string & name = output_tensors[i].getMetadata().name;
|
|
if (name == "embd_enc") {
|
|
binding->embd_enc_out_idx = i;
|
|
} else if (name == "cross_k") {
|
|
binding->cross_k_out_idx = i;
|
|
} else if (name == "cross_v") {
|
|
binding->cross_v_out_idx = i;
|
|
}
|
|
}
|
|
|
|
if (binding->embd_enc_out_idx < 0) {
|
|
std::fprintf(stderr, "%s: WARNING: embd_enc output not found by name; falling back to output[0]\n", caller);
|
|
binding->embd_enc_out_idx = 0;
|
|
}
|
|
|
|
const bool has_cross_k = binding->cross_k_out_idx >= 0;
|
|
const bool has_cross_v = binding->cross_v_out_idx >= 0;
|
|
if (has_cross_k != has_cross_v) {
|
|
return fail("Incomplete cross-projection contract: both cross_k and cross_v outputs are required");
|
|
}
|
|
|
|
if (has_cross_k && (binding->cross_k_out_idx == binding->cross_v_out_idx ||
|
|
binding->cross_k_out_idx == binding->embd_enc_out_idx ||
|
|
binding->cross_v_out_idx == binding->embd_enc_out_idx)) {
|
|
return fail("Invalid output mapping: embd_enc/cross_k/cross_v indices overlap");
|
|
}
|
|
|
|
const auto & mel_meta = input_tensors[binding->mel_in_idx].getMetadata();
|
|
if (!whisper_flexml_dtype_to_ggml_type(mel_meta.type, nullptr)) {
|
|
return fail(
|
|
std::string("Unsupported mel input type: ") +
|
|
whisper_flexml_dtype_name(mel_meta.type) + " (supported: Float32/Float16/BFloat16)");
|
|
}
|
|
binding->mel_in_expected_bytes = mel_meta.size;
|
|
|
|
const auto & embd_meta = output_tensors[binding->embd_enc_out_idx].getMetadata();
|
|
if (!whisper_flexml_dtype_to_ggml_type(embd_meta.type, nullptr)) {
|
|
return fail(
|
|
std::string("Unsupported embd_enc output type: ") +
|
|
whisper_flexml_dtype_name(embd_meta.type) + " (supported: Float32/Float16/BFloat16)");
|
|
}
|
|
binding->embd_enc_expected_bytes = embd_meta.size;
|
|
|
|
if (has_cross_k) {
|
|
const auto & cross_k_meta = output_tensors[binding->cross_k_out_idx].getMetadata();
|
|
const auto & cross_v_meta = output_tensors[binding->cross_v_out_idx].getMetadata();
|
|
if (cross_k_meta.type != flexmlrt::client::DataType::Float32 ||
|
|
cross_v_meta.type != flexmlrt::client::DataType::Float32) {
|
|
return fail(
|
|
std::string("Unsupported cross output type(s): cross_k=") +
|
|
whisper_flexml_dtype_name(cross_k_meta.type) + ", cross_v=" +
|
|
whisper_flexml_dtype_name(cross_v_meta.type) + " (cross path currently requires Float32)");
|
|
}
|
|
if (cross_k_meta.size != cross_v_meta.size) {
|
|
return fail("cross_k and cross_v output sizes do not match");
|
|
}
|
|
binding->cross_k_expected_bytes = cross_k_meta.size;
|
|
binding->cross_v_expected_bytes = cross_v_meta.size;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool whisper_vitisai_all_tensors_claimed(
|
|
const char * caller,
|
|
const char * tensor_kind,
|
|
const std::vector<flexmlrt::client::ErtTensorType> & tensors,
|
|
const std::vector<bool> & claimed) {
|
|
for (size_t i = 0; i < tensors.size(); ++i) {
|
|
if (!claimed[i]) {
|
|
std::fprintf(stderr,
|
|
"%s: unsupported extra %s tensor at index %zu (name='%s'); strict contract expects only mapped %ss\n",
|
|
caller, tensor_kind, i, tensors[i].getMetadata().name.c_str(), tensor_kind);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void whisper_kv_cross_scale_k_f32(
|
|
float * k_data,
|
|
size_t count,
|
|
float kscale) {
|
|
for (size_t i = 0; i < count; ++i) {
|
|
k_data[i] *= kscale;
|
|
}
|
|
}
|
|
|
|
void whisper_kv_cross_store_layers_f32(
|
|
const float * src_k,
|
|
const float * src_v,
|
|
uint8_t * dst_k,
|
|
uint8_t * dst_v,
|
|
const whisper_kv_cross_layout & layout) {
|
|
for (int il = 0; il < layout.n_layer; ++il) {
|
|
const float * layer_src_k = src_k + (size_t)il * layout.src_layer_elems;
|
|
const float * layer_src_v = src_v + (size_t)il * layout.src_layer_elems;
|
|
float * dk = (float *)(dst_k + layout.dst_layer_stride * (size_t)il);
|
|
float * dv = (float *)(dst_v + layout.dst_layer_stride * (size_t)il);
|
|
for (size_t i = 0; i < layout.layer_elems; ++i) {
|
|
dk[i] = layer_src_k[i] * layout.kscale;
|
|
dv[i] = layer_src_v[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void whisper_kv_cross_store_layers_f16(
|
|
const float * src_k,
|
|
const float * src_v,
|
|
uint8_t * dst_k,
|
|
uint8_t * dst_v,
|
|
const whisper_kv_cross_layout & layout) {
|
|
for (int il = 0; il < layout.n_layer; ++il) {
|
|
const float * layer_src_k = src_k + (size_t)il * layout.src_layer_elems;
|
|
const float * layer_src_v = src_v + (size_t)il * layout.src_layer_elems;
|
|
ggml_fp16_t * dk = (ggml_fp16_t *)(dst_k + layout.dst_layer_stride * (size_t)il);
|
|
ggml_fp16_t * dv = (ggml_fp16_t *)(dst_v + layout.dst_layer_stride * (size_t)il);
|
|
for (size_t i = 0; i < layout.layer_elems; ++i) {
|
|
dk[i] = ggml_fp32_to_fp16(layer_src_k[i] * layout.kscale);
|
|
dv[i] = ggml_fp32_to_fp16(layer_src_v[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void whisper_kv_cross_transpose_v_layers_f32(
|
|
const float * src_v,
|
|
uint8_t * dst_v,
|
|
const whisper_kv_cross_layout & layout) {
|
|
const int n_ctx = layout.n_ctx;
|
|
const int n_state = layout.n_state;
|
|
|
|
const int BLOCK = 32;
|
|
for (int il = 0; il < layout.n_layer; ++il) {
|
|
const float * layer_src_v = src_v + (size_t)il * layout.src_layer_elems;
|
|
float * dv = (float *)(dst_v + layout.dst_layer_stride * (size_t)il);
|
|
|
|
for (int ic = 0; ic < n_ctx; ic += BLOCK) {
|
|
for (int is = 0; is < n_state; is += BLOCK) {
|
|
const int ic_end = std::min(ic + BLOCK, n_ctx);
|
|
const int is_end = std::min(is + BLOCK, n_state);
|
|
for (int i = ic; i < ic_end; ++i) {
|
|
for (int j = is; j < is_end; ++j) {
|
|
dv[j * n_ctx + i] = layer_src_v[i * n_state + j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void whisper_kv_cross_store_k_transpose_v_layers_f16(
|
|
const float * src_k,
|
|
const float * src_v,
|
|
uint8_t * dst_k,
|
|
uint8_t * dst_v,
|
|
const whisper_kv_cross_layout & layout) {
|
|
const int n_ctx = layout.n_ctx;
|
|
const int n_state = layout.n_state;
|
|
|
|
const int BLOCK = 32;
|
|
for (int il = 0; il < layout.n_layer; ++il) {
|
|
const float * layer_src_k = src_k + (size_t)il * layout.src_layer_elems;
|
|
const float * layer_src_v = src_v + (size_t)il * layout.src_layer_elems;
|
|
ggml_fp16_t * dk = (ggml_fp16_t *)(dst_k + layout.dst_layer_stride * (size_t)il);
|
|
ggml_fp16_t * dv = (ggml_fp16_t *)(dst_v + layout.dst_layer_stride * (size_t)il);
|
|
for (size_t i = 0; i < layout.layer_elems; ++i) {
|
|
dk[i] = ggml_fp32_to_fp16(layer_src_k[i] * layout.kscale);
|
|
}
|
|
|
|
for (int ic = 0; ic < n_ctx; ic += BLOCK) {
|
|
for (int is = 0; is < n_state; is += BLOCK) {
|
|
const int ic_end = std::min(ic + BLOCK, n_ctx);
|
|
const int is_end = std::min(is + BLOCK, n_state);
|
|
for (int i = ic; i < ic_end; ++i) {
|
|
for (int j = is; j < is_end; ++j) {
|
|
dv[j * n_ctx + i] = ggml_fp32_to_fp16(layer_src_v[i * n_state + j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace whisper_vitisai_helpers
|