feat: turn the communication datatype from float32 to int8

This commit is contained in:
DandinPower
2025-08-08 21:18:33 +08:00
parent 7480ffbd30
commit ff310e7882
+132 -24
View File
@@ -97,6 +97,84 @@
#include <string>
#include <iostream>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#define Q8_0_BLOCK_SIZE 64
// One Q8_0 block = scale + 64 int8s
typedef struct {
float d; // scale
int8_t qs[Q8_0_BLOCK_SIZE]; // quantized values
} block_q8_0;
// Quantize a 1D float array into Q8_0 blocks.
// - in: pointer to N floats
// - N: number of elements
// - out_blocks: *out set to malloc'd array of blocks; caller frees
// Returns number of blocks (ceil(N/32)). On error returns 0.
int64_t q8_0_quantize(const float *in, int64_t N, block_q8_0 **out_blocks) {
if (!in || !out_blocks || N == 0) return 0;
const int64_t nb = (N + Q8_0_BLOCK_SIZE - 1) / Q8_0_BLOCK_SIZE;
block_q8_0 *blk = (block_q8_0 *)malloc(nb * sizeof(block_q8_0));
if (!blk) return 0;
for (int64_t b = 0; b < nb; ++b) {
const int64_t start = b * Q8_0_BLOCK_SIZE;
const int64_t rem = (start + Q8_0_BLOCK_SIZE <= N) ? Q8_0_BLOCK_SIZE : (N - start);
// 1) find max-abs in this block
float amax = 0.0f;
for (int64_t i = 0; i < rem; ++i) {
float v = fabsf(in[start + i]);
if (v > amax) amax = v;
}
// 2) compute scale
float d = (amax > 0.0f) ? (amax / 127.0f) : 0.0f;
float invd = (d > 0.0f) ? (1.0f / d) : 0.0f;
blk[b].d = d;
// 3) quantize present elems, zero-pad the rest
for (int64_t i = 0; i < rem; ++i) {
float r = in[start + i] * invd;
// nearest int, then clamp to [-127, 127]
long qi = lrintf(r);
if (qi < -127) qi = -127;
if (qi > 127) qi = 127;
blk[b].qs[i] = (int8_t)qi;
}
for (int64_t i = rem; i < Q8_0_BLOCK_SIZE; ++i) {
blk[b].qs[i] = 0;
}
}
*out_blocks = blk;
return nb;
}
// Dequantize Q8_0 blocks back to floats.
// - blocks: pointer to nb blocks
// - N: number of output floats desired (original length)
// - out: pointer to N floats (must be allocated by caller)
void q8_0_dequantize(const block_q8_0 *blocks, int64_t nb, int64_t N, float *out) {
if (!blocks || !out || N == 0) return;
for (int64_t b = 0; b < nb; ++b) {
const int64_t start = b * Q8_0_BLOCK_SIZE;
const int64_t rem = (start + Q8_0_BLOCK_SIZE <= N) ? Q8_0_BLOCK_SIZE : (N - start);
const float d = blocks[b].d;
for (int64_t i = 0; i < rem; ++i) {
out[start + i] = d * (float)blocks[b].qs[i];
}
}
}
int g_llama_send_tensors_counts = 0;
int g_llama_recv_tensors_counts = 0;
@@ -18131,29 +18209,42 @@ static void llama_send_tensors(zmq::socket_t & socket, struct llama_ubatch * uba
g_llama_send_tensors_counts++;
try {
std::vector<zmq::message_t> send_msgs;
size_t buf_size = 0;
send_msgs.emplace_back("sub_gf_out", strlen("sub_gf_out"));
send_msgs.emplace_back("q8_0", strlen("q8_0"));
send_msgs.emplace_back(tensors->sub_gf_out->ne, sizeof(tensors->sub_gf_out->ne));
buf_size = tensors->sub_gf_out->ne[0] * tensors->sub_gf_out->ne[1] * sizeof(float);
send_msgs.emplace_back(ubatch->backend_embd, buf_size);
if (dump_folder && strlen(dump_folder) > 0) {
std::string dump_path = std::string(dump_folder) + "/send_" + std::to_string(g_llama_send_tensors_counts) + ".bin";
dump_tensors(dump_path, static_cast<uint8_t>(TensorDataType::FLOAT32),
static_cast<uint64_t>(tensors->sub_gf_out->ne[0]),
static_cast<uint64_t>(tensors->sub_gf_out->ne[1]),
buf_size, ubatch->backend_embd);
}
// size_t buf_size = tensors->sub_gf_out->ne[0] * tensors->sub_gf_out->ne[1] * sizeof(float);
// send_msgs.emplace_back(ubatch->backend_embd, buf_size);
// q8_0 communication quantization
int64_t tensor_elements = tensors->sub_gf_out->ne[0] * tensors->sub_gf_out->ne[1];
block_q8_0 * quantized_data = NULL;
int64_t n_blocks = q8_0_quantize(ubatch->backend_embd, tensor_elements, &quantized_data);
int64_t buf_size = n_blocks * sizeof(block_q8_0);
send_msgs.emplace_back(&n_blocks, sizeof(n_blocks));
send_msgs.emplace_back(quantized_data, buf_size);
if (tensors->inp_pos) {
send_msgs.emplace_back("inp_pos", strlen("inp_pos"));
send_msgs.emplace_back("int32", strlen("int32"));
send_msgs.emplace_back(tensors->inp_pos->ne, sizeof(tensors->inp_pos->ne[0]));
buf_size = tensors->inp_pos->ne[0] * sizeof(int32_t);
send_msgs.emplace_back(0, sizeof(int64_t)); // extra frame that recv does not account for
send_msgs.emplace_back(ubatch->pos, buf_size);
}
zmq::send_multipart(socket, send_msgs);
free(quantized_data);
// TODO: Fix dump feature to support int8
// if (dump_folder && strlen(dump_folder) > 0) {
// std::string dump_path = std::string(dump_folder) + "/send_" + std::to_string(g_llama_send_tensors_counts) + ".bin";
// dump_tensors(dump_path, static_cast<uint8_t>(TensorDataType::FLOAT32),
// static_cast<uint64_t>(tensors->sub_gf_out->ne[0]),
// static_cast<uint64_t>(tensors->sub_gf_out->ne[1]),
// buf_size, ubatch->backend_embd);
// }
} catch (const zmq::error_t& e) {
LLAMA_LOG_INFO("Failed to send tensor data: %s\n", e.what());
}
@@ -18167,24 +18258,41 @@ static void llama_recv_tensors(zmq::socket_t & socket, struct llama_ubatch * uba
return;
}
for (size_t i = 0; i < recv_msgs.size(); i += 3) {
for (size_t i = 0; i < recv_msgs.size(); i += 5) {
std::string key = recv_msgs[i].to_string();
zmq::message_t &dims_msg = recv_msgs[i + 1];
zmq::message_t &data_msg = recv_msgs[i + 2];
std::string comm_type = recv_msgs[i + 1].to_string();
zmq::message_t &dims_msg = recv_msgs[i + 2];
zmq::message_t &n_blocks_msg = recv_msgs[i + 3];
zmq::message_t &data_msg = recv_msgs[i + 4];
if (key == "sub_gf_out") {
int64_t * dims = static_cast<int64_t *>(dims_msg.data());
size_t buf_size = dims[0] * dims[1] * sizeof(float);
float * batch_embd = is_out_embd ? ubatch->out_embd : ubatch->backend_embd;
std::memcpy(batch_embd, data_msg.data(), buf_size);
if (dump_folder && strlen(dump_folder) > 0) {
std::string dump_path = std::string(dump_folder) + "/recv_" + std::to_string(g_llama_recv_tensors_counts) + ".bin";
dump_tensors(dump_path, static_cast<uint8_t>(TensorDataType::FLOAT32),
static_cast<uint64_t>(dims[0]),
static_cast<uint64_t>(dims[1]),
buf_size, data_msg.data());
int64_t * n_blocks_ptr = static_cast<int64_t*>(n_blocks_msg.data());
int64_t n_blocks = *n_blocks_ptr;
int64_t buf_size = n_blocks * sizeof(block_q8_0);
block_q8_0 *quantized_data = (block_q8_0 *)malloc(n_blocks * sizeof(block_q8_0));
if (!quantized_data) {
LLAMA_LOG_INFO("Failed to allocate space for recv data.\n");
return;
}
std::memcpy(quantized_data, data_msg.data(), buf_size);
float * batch_embd = is_out_embd ? ubatch->out_embd : ubatch->backend_embd;
int64_t tensor_elements = dims[0] * dims[1];
q8_0_dequantize(quantized_data, n_blocks, tensor_elements, batch_embd);
free(quantized_data);
// size_t buf_size = dims[0] * dims[1] * sizeof(float);
// std::memcpy(batch_embd, data_msg.data(), buf_size);
// TODO: Fix dump feature to support int8
// if (dump_folder && strlen(dump_folder) > 0) {
// std::string dump_path = std::string(dump_folder) + "/recv_" + std::to_string(g_llama_recv_tensors_counts) + ".bin";
// dump_tensors(dump_path, static_cast<uint8_t>(TensorDataType::FLOAT32),
// static_cast<uint64_t>(dims[0]),
// static_cast<uint64_t>(dims[1]),
// buf_size, data_msg.data());
// }
} else if (key == "inp_pos") {
int64_t * dims = static_cast<int64_t *>(dims_msg.data());
size_t buf_size = dims[0] * sizeof(int32_t);