Book contents +

XII · Apple acceleration (welvet.metal) / CHAPTER 77

Apple Metal: mount and forward

Samuel Watson · Welvet feature bookOriginal source ↗

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

Run: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

exit 0 · last run via go run .

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 .