XII · Apple acceleration (welvet.metal) / CHAPTER 77
Apple Metal: mount and forward
github.com/openfluke/welvet/nativemetalverified Apple M5
Why it exists
Run a Go layer on Apple GPU with an explicit lifetime.
What it is
Mount an identity Dense layer, inspect the Metal version and device, and forward a Float32 tensor.
Session ownership
Create a session with nativemetal.Mount, call Forward repeatedly and close it when finished. Mount accepts supported layers, Stack, Parallel and Grid layouts. The library is independent of welvet.cpp.
Requirements and backend boundaries
Go Welvet v1.3.0 uses the independent welvet.metal 0.68 library (mounted model ABI 1, segment extension 1). Install the library from its checkout:
cd ../welvet.metal
./scripts/macos_install_go.sh
Execution requires Apple silicon macOS and Go with cgo. Set WELVET_METAL_LIBRARY to override library discovery. Ordinary Go CPU use on Linux or Windows does not require Apple hardware. Unsupported hosts print SKIP in these examples; missing libraries on supported hosts report an error.
Go schedules work and manages memory; numerical Metal forward, backward and weight updates execute custom Apple GPU kernels. This is GPU acceleration through Metal, not Apple Neural Engine execution. Activations are Float32; FormatNone parameter storage supports 34 dtypes, with GDN restricted to Float32. Small examples demonstrate APIs; GPU launch costs can outweigh useful work at small shapes.
cpu-tiled means Go CPU; native_cpu means the separate welvet.cpp library; metal means the separate Apple GPU library. Neither native engine depends on the other. Examples 82-86 can use go run -tags welvet_metal . to exclude C++ linkage. Chapter 87 requires the normal cgo build for both native engines.
Go example
examples/82-apple-metal/main.go
cd welvet/examples/82-apple-metal && source ../env.sh && go run .// Mount one layer, execute its forward pass on Apple Metal, and release it.
package main
import (
"flag"
"fmt"
"github.com/openfluke/welvet/core"
"github.com/openfluke/welvet/layers/dense"
"github.com/openfluke/welvet/nativemetal"
"github.com/openfluke/welvet/quant"
"log"
"runtime"
)
func main() {
if e := run(); e != nil {
log.Fatal(e)
}
}
func run() error {
library := flag.String("library", "", "optional libwelvet_metal.dylib path")
flag.Parse()
if runtime.GOOS != "darwin" || runtime.GOARCH != "arm64" {
fmt.Println("SKIP: Apple Metal needs Apple silicon macOS and cgo")
return nil
}
l, e := dense.NewConfigured(2, 2, core.ActivationLinear, core.DTypeFloat32, quant.FormatNone, []float32{1, 0, 0, 1})
if e != nil {
return e
}
session, e := nativemetal.Mount(l, nativemetal.Options{LibraryPath: *library})
if e != nil {
return e
}
defer session.Close()
x := &core.Tensor[float32]{Shape: []int{1, 2}, Data: []float32{.5, -.25}}
post, e := session.Forward(x)
if e != nil {
return e
}
fmt.Printf("backend=metal version=%s\ndevice=%s\ninput=%v output=%v shape=%v\n", session.Version(), session.Identity(), x.Data, post.Data, post.Shape)
return nil
}
Output
backend=metal version=0.68
device=metal:Apple M5:reg=4294968322:ws=19069665280
input=[0.5 -0.25] output=[0.5 -0.25] shape=[1 2]
Validate (harness)
cd welvet/examples/82-apple-metal && source ../env.sh && go run .