ferrum-kernels 0.8.7

Unified compute kernels (CUDA/Metal/CPU) and model runner for Ferrum inference
Documentation
#include <metal_stdlib>
using namespace metal;

constant uint QK8_0 = 32;

struct LinearParams {
    uint rows;
    uint in_features;
    uint out_features;
    uint output_stride;
    uint output_column_offset;
};

struct SwiGluParams {
    uint rows;
    uint intermediate_size;
    uint gate_up_stride;
};

struct block_q8_0 {
    half d;
    char qs[QK8_0];
};

static inline float q8_0_value(device const block_q8_0 & block, uint index) {
    return float(block.d) * float(block.qs[index]);
}

kernel void vnext_linear_dense_f16(
    device const half * input [[buffer(0)]],
    device const half * weight [[buffer(1)]],
    device half * output [[buffer(2)]],
    constant LinearParams & params [[buffer(3)]],
    uint3 group [[threadgroup_position_in_grid]],
    uint simd_lane [[thread_index_in_simdgroup]],
    uint simd_group [[simdgroup_index_in_threadgroup]]) {
    const uint row = group.y;
    const uint first_output = group.x * 4 + simd_group * 2;
    float sums[2] = {0.0f, 0.0f};
    const ulong input_base = ulong(row) * params.in_features;
    for (uint column = simd_lane; column < params.in_features; column += 32) {
        const float activation = float(input[input_base + column]);
        for (uint local_output = 0; local_output < 2; ++local_output) {
            const uint output_column = first_output + local_output;
            if (output_column < params.out_features) {
                sums[local_output] += activation
                    * float(weight[ulong(output_column) * params.in_features + column]);
            }
        }
    }
    for (uint local_output = 0; local_output < 2; ++local_output) {
        const uint output_column = first_output + local_output;
        const float total = simd_sum(sums[local_output]);
        if (simd_lane == 0 && output_column < params.out_features) {
            output[ulong(row) * params.output_stride
                + params.output_column_offset + output_column] = half(total);
        }
    }
}

kernel void vnext_linear_q8_0_f16(
    device const half * input [[buffer(0)]],
    device const block_q8_0 * weight [[buffer(1)]],
    device half * output [[buffer(2)]],
    constant LinearParams & params [[buffer(3)]],
    uint3 group [[threadgroup_position_in_grid]],
    uint simd_lane [[thread_index_in_simdgroup]],
    uint simd_group [[simdgroup_index_in_threadgroup]]) {
    const uint row = group.y;
    const uint first_output = group.x * 4 + simd_group * 2;
    const uint blocks_per_row = params.in_features / QK8_0;
    float sums[2] = {0.0f, 0.0f};
    const ulong input_base = ulong(row) * params.in_features;
    for (uint column = simd_lane; column < params.in_features; column += 32) {
        const float activation = float(input[input_base + column]);
        for (uint local_output = 0; local_output < 2; ++local_output) {
            const uint output_column = first_output + local_output;
            if (output_column < params.out_features) {
                const ulong weight_base = ulong(output_column) * blocks_per_row;
                sums[local_output] += activation * q8_0_value(
                    weight[weight_base + column / QK8_0], column % QK8_0
                );
            }
        }
    }
    for (uint local_output = 0; local_output < 2; ++local_output) {
        const uint output_column = first_output + local_output;
        const float total = simd_sum(sums[local_output]);
        if (simd_lane == 0 && output_column < params.out_features) {
            output[ulong(row) * params.output_stride
                + params.output_column_offset + output_column] = half(total);
        }
    }
}

kernel void vnext_swiglu_f16(
    device const half * gate_up [[buffer(0)]],
    device half * activation [[buffer(1)]],
    constant SwiGluParams & params [[buffer(2)]],
    uint index [[thread_position_in_grid]]) {
    const uint elements = params.rows * params.intermediate_size;
    if (index >= elements) {
        return;
    }
    const uint row = index / params.intermediate_size;
    const uint column = index % params.intermediate_size;
    const ulong base = ulong(row) * params.gate_up_stride;
    const float gate = float(gate_up[base + column]);
    const float up = float(gate_up[base + params.intermediate_size + column]);
    activation[index] = half((gate / (1.0f + exp(-gate))) * up);
}

kernel void vnext_linear_dense_f32(
    device const float * input [[buffer(0)]],
    device const half * weight [[buffer(1)]],
    device float * output [[buffer(2)]],
    constant LinearParams & params [[buffer(3)]],
    uint3 group [[threadgroup_position_in_grid]],
    uint simd_lane [[thread_index_in_simdgroup]],
    uint simd_group [[simdgroup_index_in_threadgroup]]) {
    const uint row = group.y;
    const uint first_output = group.x * 4 + simd_group * 2;
    float sums[2] = {0.0f, 0.0f};
    const ulong input_base = ulong(row) * params.in_features;
    for (uint column = simd_lane; column < params.in_features; column += 32) {
        const float activation = input[input_base + column];
        for (uint local_output = 0; local_output < 2; ++local_output) {
            const uint output_column = first_output + local_output;
            if (output_column < params.out_features) {
                sums[local_output] += activation
                    * float(weight[ulong(output_column) * params.in_features + column]);
            }
        }
    }
    for (uint local_output = 0; local_output < 2; ++local_output) {
        const uint output_column = first_output + local_output;
        const float total = simd_sum(sums[local_output]);
        if (simd_lane == 0 && output_column < params.out_features) {
            output[ulong(row) * params.output_stride
                + params.output_column_offset + output_column] = total;
        }
    }
}

kernel void vnext_linear_q8_0_f32(
    device const float * input [[buffer(0)]],
    device const block_q8_0 * weight [[buffer(1)]],
    device float * output [[buffer(2)]],
    constant LinearParams & params [[buffer(3)]],
    uint3 group [[threadgroup_position_in_grid]],
    uint simd_lane [[thread_index_in_simdgroup]],
    uint simd_group [[simdgroup_index_in_threadgroup]]) {
    const uint row = group.y;
    const uint first_output = group.x * 4 + simd_group * 2;
    const uint blocks_per_row = params.in_features / QK8_0;
    float sums[2] = {0.0f, 0.0f};
    const ulong input_base = ulong(row) * params.in_features;
    for (uint column = simd_lane; column < params.in_features; column += 32) {
        const float activation = input[input_base + column];
        for (uint local_output = 0; local_output < 2; ++local_output) {
            const uint output_column = first_output + local_output;
            if (output_column < params.out_features) {
                const ulong weight_base = ulong(output_column) * blocks_per_row;
                sums[local_output] += activation * q8_0_value(
                    weight[weight_base + column / QK8_0], column % QK8_0
                );
            }
        }
    }
    for (uint local_output = 0; local_output < 2; ++local_output) {
        const uint output_column = first_output + local_output;
        const float total = simd_sum(sums[local_output]);
        if (simd_lane == 0 && output_column < params.out_features) {
            output[ulong(row) * params.output_stride
                + params.output_column_offset + output_column] = total;
        }
    }
}