mirror of
https://github.com/hanzoai/chains.git
synced 2026-10-30 18:46:11 +00:00
* hanzo-evm: aiquorum + aimarket precompiles, evmllm (geth AI precompile + e2e), operator/canonical (commit/reveal, int8-quant), aiffi bench. Thinking-chain substrate. * hanzo-evm/evm: wire AI precompiles (inference 0x0300..03 + modelregistry) Registers deterministic int8 inference (Tier-1 in-consensus AI) + ModelSpec registry into the Hanzo EVM. threshold dep aligned to v1.9.4 (go.mod match). Builds green (55M binary). aivmbridge follows luxfi/precompile release.
173 lines
4.9 KiB
Go
173 lines
4.9 KiB
Go
//go:build !runtime
|
|
|
|
// Command aiffi-bench measures the Go EVM's LLM + embedding precompile
|
|
// integration overhead, at parity with the Rust VM: it calls the native Hanzo
|
|
// engine over cgo (the same engine + models the Rust VM uses) for BOTH text
|
|
// generation and embeddings, routed by model name, and times them.
|
|
//
|
|
// In-process, load-once, no IPC — the path a Go EVM precompile's Run() uses.
|
|
//
|
|
// Run (needs the cdylib built with `accelerate`):
|
|
//
|
|
// HANZO_FFI_MODELS="zen-nano=gguf:/tmp/zen5-weights/zen-5-flash.gguf;zen-embed=embedding:/tmp/zen-embedding-0.6B" \
|
|
// HANZO_FFI_TOK_DIR=/tmp/zen-nano-fused INFER_MODEL=zen-nano EMBED_MODEL=zen-embed BENCH_ITERS=8 \
|
|
// GOWORK=off CGO_ENABLED=1 go run .
|
|
package main
|
|
|
|
/*
|
|
#cgo CFLAGS: -I${SRCDIR}/../../../engine/hanzo-engine-ffi/include
|
|
#cgo LDFLAGS: -L${SRCDIR}/../../../engine/target/release -lhanzo_engine_ffi -Wl,-rpath,${SRCDIR}/../../../engine/target/release
|
|
#include "hanzo_engine_ffi.h"
|
|
#include <stdlib.h>
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
"unsafe"
|
|
)
|
|
|
|
func bytePtr(b []byte) *C.uint8_t {
|
|
if len(b) == 0 {
|
|
return nil
|
|
}
|
|
return (*C.uint8_t)(unsafe.Pointer(&b[0]))
|
|
}
|
|
|
|
func ready() bool { return C.hanzo_ffi_ready() == 1 }
|
|
|
|
// infer runs native text generation on model `model` (empty = default).
|
|
func infer(model, prompt string) (string, error) {
|
|
mb, pb := []byte(model), []byte(prompt)
|
|
var out *C.uint8_t
|
|
var outLen C.size_t
|
|
rc := C.hanzo_ffi_infer(bytePtr(mb), C.size_t(len(mb)), bytePtr(pb), C.size_t(len(pb)), &out, &outLen)
|
|
if rc != 0 {
|
|
return "", fmt.Errorf("hanzo_ffi_infer rc=%d", int(rc))
|
|
}
|
|
defer C.hanzo_ffi_free(out, outLen)
|
|
return string(C.GoBytes(unsafe.Pointer(out), C.int(outLen))), nil
|
|
}
|
|
|
|
// embed runs a native embedding on model `model` (empty = default).
|
|
func embed(model, text string) ([]float32, error) {
|
|
mb, tb := []byte(model), []byte(text)
|
|
var out *C.float
|
|
var count C.size_t
|
|
rc := C.hanzo_ffi_embed(bytePtr(mb), C.size_t(len(mb)), bytePtr(tb), C.size_t(len(tb)), &out, &count)
|
|
if rc != 0 {
|
|
return nil, fmt.Errorf("hanzo_ffi_embed rc=%d", int(rc))
|
|
}
|
|
defer C.hanzo_ffi_free_f32(out, count)
|
|
src := unsafe.Slice((*float32)(unsafe.Pointer(out)), int(count))
|
|
v := make([]float32, int(count))
|
|
copy(v, src)
|
|
return v, nil
|
|
}
|
|
|
|
func mean(xs []float64) float64 {
|
|
var s float64
|
|
for _, x := range xs {
|
|
s += x
|
|
}
|
|
if len(xs) == 0 {
|
|
return 0
|
|
}
|
|
return s / float64(len(xs))
|
|
}
|
|
func minf(xs []float64) float64 {
|
|
if len(xs) == 0 {
|
|
return 0
|
|
}
|
|
m := xs[0]
|
|
for _, x := range xs {
|
|
if x < m {
|
|
m = x
|
|
}
|
|
}
|
|
return m
|
|
}
|
|
|
|
func main() {
|
|
iters := 8
|
|
if v := os.Getenv("BENCH_ITERS"); v != "" {
|
|
if n, err := strconv.Atoi(v); err == nil && n > 0 {
|
|
iters = n
|
|
}
|
|
}
|
|
inferModel := os.Getenv("INFER_MODEL")
|
|
embedModel := os.Getenv("EMBED_MODEL")
|
|
prompt := "Who are you and who made you? Answer in one sentence."
|
|
text := "The quick brown fox jumps over the lazy dog."
|
|
|
|
fmt.Println("loading native engine + models via cgo (one-time)...")
|
|
if !ready() {
|
|
fmt.Println("ERROR: engine not ready (set HANZO_FFI_MODELS)")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Warm + capability probe.
|
|
out, err := infer(inferModel, prompt)
|
|
if err != nil {
|
|
fmt.Println("ERROR infer:", err)
|
|
os.Exit(1)
|
|
}
|
|
outBytes := len(out)
|
|
emb, embErr := embed(embedModel, text)
|
|
|
|
var inferMs, embedMs, floor []float64
|
|
for i := 0; i < iters; i++ {
|
|
t := time.Now()
|
|
if _, err := infer(inferModel, prompt); err != nil {
|
|
fmt.Println("ERROR infer:", err)
|
|
os.Exit(1)
|
|
}
|
|
inferMs = append(inferMs, float64(time.Since(t).Microseconds())/1000.0)
|
|
|
|
if embErr == nil {
|
|
t2 := time.Now()
|
|
if _, err := embed(embedModel, text); err != nil {
|
|
fmt.Println("ERROR embed:", err)
|
|
os.Exit(1)
|
|
}
|
|
embedMs = append(embedMs, float64(time.Since(t2).Microseconds())/1000.0)
|
|
}
|
|
|
|
t3 := time.Now()
|
|
_ = ready()
|
|
floor = append(floor, float64(time.Since(t3).Nanoseconds())/1000.0) // µs
|
|
}
|
|
|
|
fmt.Printf("\n==== GO VM — infer + embed precompile benchmark (%d iters, warm, cgo, CPU+Accelerate) ====\n", iters)
|
|
im := inferModel
|
|
if im == "" {
|
|
im = "default"
|
|
}
|
|
fmt.Printf("[infer model=%q] cgo %.1f ms (min %.1f) | out %d B\n", im, mean(inferMs), minf(inferMs), outBytes)
|
|
embJSON := ""
|
|
if embErr == nil {
|
|
em := embedModel
|
|
if em == "" {
|
|
em = "default"
|
|
}
|
|
fmt.Printf("[embed model=%q] cgo %.1f ms (min %.1f) | dim %d\n", em, mean(embedMs), minf(embedMs), len(emb))
|
|
embJSON = fmt.Sprintf(",\"embed_vm_ms\":%.3f,\"embed_dim\":%d", mean(embedMs), len(emb))
|
|
} else {
|
|
fmt.Printf("[embed] unavailable: %v\n", embErr)
|
|
}
|
|
fmt.Printf("pure cgo floor : %.2f µs (boundary crossing, no model)\n", mean(floor))
|
|
fmt.Printf("sample output : %q\n", truncate(out, 80))
|
|
fmt.Printf("\nBENCH_JSON {\"lang\":\"go\",\"iters\":%d,\"infer_vm_ms\":%.3f,\"cgo_floor_us\":%.2f,\"infer_out_bytes\":%d%s}\n",
|
|
iters, mean(inferMs), mean(floor), outBytes, embJSON)
|
|
}
|
|
|
|
func truncate(s string, n int) string {
|
|
if len(s) <= n {
|
|
return s
|
|
}
|
|
return s[:n] + "..."
|
|
}
|