IKNN-Rl1-A1 / kernels /satu1_avx512.cpp
deeprcurs-staff's picture
Upload kernels/satu1_avx512.cpp with huggingface_hub
a98f915 verified
Raw History Blame Contribute Delete
5.89 kB
// satu1_avx512.cpp — IKNN-Rl1-A1 — SatU1 AVX-512 Kernel (Native VPOPCNTDQ)
// Version: v1.0
// Created: 2026-09-03T16:55:00+07:00
// Last Updated: 2026-09-03T16:55:00+07:00
// Status: PUBLISHABLE — EN ONLY — M1 Kernel Validation
// Repo: IKNN-Rl1-A1 — Integrated Knowledge-phase Neural Network — Recursive Language Iteration 1 — Architecture 1
// Hardware Target: Intel Xeon / Sapphire Rapids — AVX512F, BW, VL, VBMI2, VNNI, BITALG, VPOPCNTDQ (this sandbox)
// — 2 vCPU, 54MB L3, DDR 50-90GB/s, 8GB swap at /home/user/.cache/swap-8gb (excluded)
// Description: SatU1 1-bit XNOR+POPCOUNT, 512 connections per cycle per lane, 4 cycles per 512-bit
// Canonical mapping: bit 0 = -1, bit 1 = +1, XNOR = NOT(A XOR B), S = popcount(XNOR), Y = (2S-K)*alpha
// DRSS: direct-register streaming, no intermediate buffer
// RoPE handling: RoPE applied on FP16 before Sign (see 5.4 step 3a)
#include "satu1_common.h"
#include <cstdint>
#include <immintrin.h>
namespace iknn {
namespace satu1 {
namespace avx512 {
// AVX-512 native kernel — 8x uint64_t = 512 bits = 512 weights per iteration
// Requires: AVX512F + AVX512BW + AVX512_VPOPCNTDQ + AVX512_BITALG (for _mm512_xnor? we emulate via xor+not)
inline float compute_satu1_block_avx512(
const uint64_t* __restrict__ activation_bits,
const uint64_t* __restrict__ weight_bits,
float alpha,
int block_size_words) // block_size_words = number of 64-bit words, must be multiple of 8 for 512-bit
{
// Accumulate popcount in 64-bit lanes
__m512i total_pop = _mm512_setzero_si512();
int i = 0;
// Process 8 words = 512 bits per iteration
for (; i + 7 < block_size_words; i += 8) {
__m512i a = _mm512_loadu_si512(reinterpret_cast<const __m512i*>(&activation_bits[i]));
__m512i w = _mm512_loadu_si512(reinterpret_cast<const __m512i*>(&weight_bits[i]));
// XNOR = NOT(A XOR B)
__m512i x = _mm512_xor_si512(a, w);
__m512i xnor = _mm512_xor_si512(x, _mm512_set1_epi64(-1)); // NOT via XOR with all 1s (BITALG: _mm512_xnor would be better if available)
// Native popcount per 64-bit lane — VPOPCNTDQ — 1 cycle
__m512i cnt = _mm512_popcnt_epi64(xnor);
total_pop = _mm512_add_epi64(total_pop, cnt);
}
// Tail handling for remaining words (<8)
int32_t tail_pop = 0;
for (; i < block_size_words; ++i) {
uint64_t xnor = ~(activation_bits[i] ^ weight_bits[i]);
tail_pop += __builtin_popcountll(xnor);
}
// Horizontal sum of 8x 64-bit lanes
// _mm512_reduce_add_epi64 requires AVX-512, use manual extract
alignas(64) uint64_t pop_array[8];
_mm512_store_si512(reinterpret_cast<__m512i*>(pop_array), total_pop);
int64_t total = tail_pop;
for (int j = 0; j < 8; ++j) total += pop_array[j];
int64_t signed_sum = (2 * total) - (static_cast<int64_t>(block_size_words) * 64);
return static_cast<float>(signed_sum) * alpha;
}
// Variant for 512-bit block (8 words) — most efficient, 1 ZMM = 512 weights
inline float compute_satu1_zmm512(
const __m512i& activation_zmm, // 512 bits = 8x64 bits packed signs
const __m512i& weight_zmm,
float alpha)
{
__m512i x = _mm512_xor_si512(activation_zmm, weight_zmm);
__m512i xnor = _mm512_xor_si512(x, _mm512_set1_epi64(-1));
__m512i cnt = _mm512_popcnt_epi64(xnor);
// reduce
int64_t total = _mm512_reduce_add_epi64(cnt);
int64_t signed_sum = (2 * total) - 512;
return static_cast<float>(signed_sum) * alpha;
}
} // namespace avx512
} // namespace satu1
} // namespace iknn
// Standalone test harness (when compiled as executable)
#ifdef SATU1_AVX512_TEST
#include <random>
#include <iostream>
#include <chrono>
#include <cmath>
int main() {
using namespace iknn::satu1;
using namespace iknn::satu1::avx512;
std::cout << "[SatU1 AVX-512 Test] " << __DATE__ << " " << __TIME__ << std::endl;
std::cout << "Hardware: Xeon AVX-512 VPOPCNTDQ, VBMI2" << std::endl;
// Correctness test
std::mt19937_64 rng(42);
const int WORDS = 8; // 512 bits
uint64_t act[WORDS], w[WORDS];
for (int i = 0; i < WORDS; ++i) {
act[i] = rng();
w[i] = rng();
}
float alpha = 0.5f;
float naive = compute_satu1_block_naive(act, w, alpha, WORDS);
float avx512_res = compute_satu1_block_avx512(act, w, alpha, WORDS);
std::cout << "Naive: " << naive << " AVX-512: " << avx512_res << std::endl;
if (std::abs(naive - avx512_res) < 1e-3f) {
std::cout << "[PASS] Correctness" << std::endl;
} else {
std::cout << "[FAIL] Correctness mismatch!" << std::endl;
return 1;
}
// Throughput benchmark
const int ITERS = 1000000;
auto start = std::chrono::high_resolution_clock::now();
float sum = 0;
for (int it = 0; it < ITERS; ++it) {
// Vary data to prevent optimization
act[0] ^= it;
sum += compute_satu1_block_avx512(act, w, alpha, WORDS);
}
auto end = std::chrono::high_resolution_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
double giga_pop = (double)ITERS * WORDS * 64 / 1e9;
double sec = ms / 1000.0;
double giga_per_sec = giga_pop / sec;
double tps_equiv = (double)ITERS / sec; // tokens per second if 1 block = 1 token (simplified)
std::cout << "[BENCH] Iters: " << ITERS << " Time: " << ms << " ms" << std::endl;
std::cout << "[BENCH] Giga-popcnt/s: " << giga_per_sec << std::endl;
std::cout << "[BENCH] TPS equiv (512 weights): " << tps_equiv << std::endl;
std::cout << "[BENCH] Sum (prevent opt): " << sum << std::endl;
// Expected: On Xeon 2.6GHz with VPOPCNTDQ, ~4 cycles per 512 bits => ~0.65e9 popcnt per core per GHz? Rough.
// Realistic target: >2 Giga-popcnt/s per core
return 0;
}
#endif