音乐播放器
sola的小屋
 
文章 标签
20

Powered by Gridea | Theme: Fog
载入天数...
载入时分秒...
总访问量:  |   访问人数:

[InferLLM大模型推理框架项目](20)kern中naive模块的概述(src/kern/naive)

InferLLM 框架中 naive 模块的代码结构与功能分析

naive 模块是 InferLLM 框架中的基础计算实现,提供了不依赖特定硬件优化的计算内核。这个模块作为其他优化实现的基础和后备方案,确保在任何平台上都能正常运行。

1. 目录结构

naive 模块包含以下文件:

kern/naive/
├── naive.cpp
├── naive.h
└── quantize.h
  • naive.h:声明各种计算函数和注册内核
  • naive.cpp:实现各种计算函数
  • quantize.h:实现量化和反量化相关的函数

2. 核心文件分析

2.1 naive.h

naive.h 文件定义了各种计算函数的声明,并使用 PartialImplementKernelPartialImplementSpace 宏注册这些函数到内核系统中:

namespace inferllm {
namespace naive {

// 函数声明
TaskSet llm_embedding_get_int4_float(...);
TaskSet llm_elemwise_compute_float(...);
// ... 其他函数声明

// 模板类定义
template <KernelID Id, typename... Args>
struct Comp {
    static TaskSet get_all_task(Args... args);
};

template <KernelID Id, typename... Args>
struct Space {
    static size_t get(Args... args);
};

// 注册内核
PartialImplementKernel(ElemwiseFloat, llm_elemwise_compute_float);
PartialImplementKernel(RmsNormFloat, llm_rms_norm_compute_float);
// ... 其他内核注册

// 注册工作空间计算函数
PartialImplementSpace(MatmulInt4Float, llm_matmul_get_workspace_float);
// ... 其他工作空间注册

}  // namespace naive

namespace opt {
// 优化实现的模板类,如果没有优化实现则回退到 naive 实现
template <KernelID Id, typename... Args>
struct Comp {
    static TaskSet get_all_task(Args... args) {
        return naive::Comp<Id, Args...>::get_all_task(std::forward<Args>(args)...);
    }
};

template <KernelID Id, typename... Args>
struct Space {
    static size_t get(Args... args) {
        return naive::Space<Id, Args...>::get(std::forward<Args>(args)...);
    }
};
}  // namespace opt

}  // namespace inferllm

2.2 naive.cpp

naive.cpp 文件实现了各种计算函数,每个函数都返回一个 TaskSet,用于多线程执行:

namespace inferllm {
namespace naive {

// 嵌入查找
TaskSet llm_embedding_get_int4_float(
        const void* weights, const uint32_t* index, float* dst, uint32_t len_seq,
        uint32_t embd) {
    auto task = [=](const TaskId& id) {
        for (uint32_t i = id.start; i < id.end; ++i) {
            const int row = index[i];
            const int weight_stride =
                    embd * dtype_in_byte(DType::Int4) / dtype_block_size(DType::Int4);
            dequantize_row_q4_0_reference(
                    (static_cast<const char*>(weights) + row * weight_stride),
                    dst + i * embd, embd);
        }
    };
    return TaskSet{{task, len_seq}};
}

// ... 其他函数实现
}  // namespace naive
}  // namespace inferllm

3. 功能分析

通过分析代码,可以看出 naive 模块实现了以下主要功能:

3.1 嵌入查找

TaskSet llm_embedding_get_int4_float(...);
TaskSet llm_embedding_get_int8_float(...);
TaskSet llm_embedding_get_float_float(...);

这些函数从嵌入表中查找 token 的嵌入向量,支持不同的数据类型(int4、int8、float)。

3.2 元素级操作

TaskSet llm_elemwise_compute_float(...);
TaskSet llm_elemwise_compute_float_scale(...);
TaskSet llm_elemwise_broadcast_dim0_src1_compute_float(...);

这些函数实现了元素级操作,如加法、乘法、Silu 和 Gelu 激活函数,以及带有广播的元素级操作。

3.3 归一化

TaskSet llm_norm_compute_float(...);
TaskSet llm_rms_norm_compute_float(...);

这些函数实现了层归一化和 RMS 归一化,用于稳定网络训练和推理。

3.4 Softmax

TaskSet llm_softmax_compute_float(...);

实现了 Softmax 函数,用于将输出转换为概率分布。

3.5 矩阵乘法

TaskSet llm_matmul_compute_int4_float(...);
TaskSet llm_matmul_compute_int4_float_packed(...);
TaskSet llm_matmul_compute_int8_float(...);
TaskSet llm_matmul_compute_float_float(...);

这些函数实现了不同精度的矩阵乘法,支持 int4、int8 和 float 数据类型,以及带有打包优化的版本。

3.6 注意力计算

TaskSet llm_matmul_compute_with_head_stride_float(...);
TaskSet llm_matmul_compute_with_head_strideq_broadcastk_float(...);
TaskSet llm_head_batched_matmul_compute_float(...);
TaskSet llm_head_batched_matmul_broadcastv_float(...);

这些函数实现了自注意力机制所需的矩阵运算,包括多头注意力和多查询注意力。

3.7 位置编码

TaskSet llm_rope_compute_float(...);
TaskSet llm_glm_rope_compute_float(...);

这些函数实现了旋转位置编码(RoPE)和 GLM 模型特定的旋转位置编码。

3.8 掩码操作

TaskSet llm_diag_mask_inf_float(...);
TaskSet llm_glm_gmask_inf_float(...);
TaskSet llm_scale_diag_mask_inf_float(...);

这些函数实现了注意力掩码操作,用于控制注意力的范围。

3.9 排列操作

TaskSet llm_permute_compute_float(...);

实现了张量的维度排列操作。

3.10 权重重排

TaskSet llm_int4_matmul_weight_reorder(...);

实现了 int4 权重的重排操作,用于优化矩阵乘法。

4. 实现细节分析

4.1 任务分解与多线程

每个计算函数都返回一个 TaskSet,包含一个或多个任务及其子任务数量。每个任务都是一个 lambda 函数,接受一个 TaskId 参数,包含任务的起始索引、结束索引和线程 ID。

auto task = [=](const TaskId& id) {
    for (uint32_t i = id.start; i < id.end; i++) {
        // 计算逻辑
    }
};
return TaskSet{{task, len}};

这种设计使得计算任务可以在多线程环境中高效执行,通过将大型计算任务分解为多个子任务,并分配给不同的线程处理。

4.2 量化计算

naive 模块支持 int4 和 int8 量化计算,通过 quantize.h 中定义的函数进行量化和反量化操作:

// 量化操作
quantize_row_q8_0_reference(src1 + m * K, q_src1, K);

// 反量化操作
dequantize_row_q4_0_reference(
        (static_cast<const char*>(weights) + row * weight_stride),
        dst + i * embd, embd);

量化计算可以减少内存占用和计算量,同时保持计算精度。

4.3 矩阵乘法优化

矩阵乘法是深度学习中最耗时的操作之一,naive 模块通过以下方式优化矩阵乘法:

  1. 量化计算:使用 int4 和 int8 量化减少内存占用和计算量
  2. 打包优化llm_matmul_compute_int4_float_packed 函数实现了打包优化,一次处理多个权重
  3. 工作空间优化:使用工作空间进行中间计算,减少内存分配和释放的开销

4.4 注意力计算优化

注意力计算是 Transformer 模型的核心,naive 模块通过以下方式优化注意力计算:

  1. 多头并行:将不同的注意力头分配给不同的线程处理
  2. 内存布局优化:使用合适的内存布局减少内存访问开销
  3. 广播优化:对于多查询注意力,使用广播优化减少内存占用和计算量

5. 设计模式分析

naive 模块采用了多种设计模式:

5.1 策略模式

通过 CompSpace 模板类,可以根据内核 ID 选择不同的计算策略。

5.2 模板方法模式

通过 PartialImplementKernelPartialImplementSpace 宏定义的模板类,提供了统一的接口,而具体实现由不同的函数提供。

5.3 命令模式

通过 lambda 函数和 TaskSet,将计算任务封装为命令对象,由线程池执行。

5.4 回退模式

通过 opt 命名空间中的模板类,如果没有优化实现则回退到 naive 实现,确保在任何平台上都能正常运行。

6. 性能考虑

虽然 naive 模块没有使用特定硬件的优化指令,但它仍然考虑了性能优化:

  1. 多线程并行:通过任务分解和 TaskSet,支持多线程并行计算
  2. 量化计算:支持 int4 和 int8 量化,减少内存占用和计算量
  3. 内存布局优化:使用合适的内存布局减少内存访问开销
  4. 算法优化:使用优化的算法实现各种计算操作

总结

naive 模块是 InferLLM 框架中的基础计算实现,提供了不依赖特定硬件优化的计算内核。它实现了大语言模型推理所需的各种计算操作,包括嵌入查找、元素级操作、归一化、Softmax、矩阵乘法、注意力计算、位置编码、掩码操作、排列操作和权重重排等。

通过任务分解和多线程、量化计算、矩阵乘法优化和注意力计算优化等技术,naive 模块在不依赖特定硬件优化的情况下,仍然能够提供相对高效的计算性能。同时,它作为其他优化实现的基础和后备方案,确保在任何平台上都能正常运行。

naive 模块的设计体现了 InferLLM 框架的可移植性和可扩展性,通过提供统一的接口和基础实现,使得框架可以在不同平台上运行,并且可以根据需要添加特定平台的优化实现。