All articles
AI News

H3-metal: Running Minimax-H3 Natively on Apple Silicon GPUs

FDE Coach EditorialAugust 12, 202612 min read

What Exactly Is H3-metal?

Let’s cut through the noise. H3-metal is a C library that implements the Minimax-H3 hash function—a fast, high-quality non-cryptographic hash—with a critical twist: it ships with a Metal shader backend that runs inference directly on Apple Silicon GPUs. The project, written by Salvatore Sanfilippo (the creator of Redis), is a masterclass in systems thinking. It’s not a machine learning framework. It’s not a model server. It’s a single-purpose, brutally optimized piece of code that asks: what if we could compute hashes at GPU bandwidth speeds on the hardware you already own?

The Minimax-H3 algorithm itself is a relatively recent entrant in the hash function space, designed for minimal collisions and maximum speed when processing string keys. H3-metal takes that algorithm and compiles it into a Metal compute kernel. The result is a native library you can link against from any C, Objective-C, or Swift project—no Python, no PyTorch, no ONNX runtime required. You call a function, the GPU does the work, and you get your hashes back.

Why Should Engineers Care?

Three reasons, and they’re all practical.

First, latency. CPU-based hashing of large datasets is bound by core count and memory bandwidth. Apple Silicon’s unified memory architecture means the GPU can access the same physical memory as the CPU. H3-metal exploits this ruthlessly. Data sits in a buffer, the GPU kernel processes it in parallel, and results land in another buffer. No copies, no PCIe transfers. For batch sizes in the millions, you’re looking at orders-of-magnitude speedups over scalar CPU implementations.

Second, it’s a blueprint. H3-metal demonstrates a pattern that’s directly transferable to other compute-bound tasks: write the hot loop in Metal Shading Language, wrap it in a minimal C API, and ship it as a standalone library. If you’re an engineer who’s ever wrestled with Core ML’s overhead or Metal Performance Shaders’ complexity for simple element-wise operations, this is a breath of fresh air. It proves you don’t need a heavyweight framework to tap into the GPU.

Third, it’s production-grade thinking from a legendary engineer. Sanfilippo didn’t build this as a toy. The code is clean, the build system is trivial (make), and the API surface is deliberately tiny. There’s a lesson here about scope discipline: H3-metal does one thing and does it well. No feature creep, no dependency hell.

The Architecture: How It Works Under the Hood

Let’s trace the execution path. Understanding this flow will help you reason about where it fits in your own systems.

Here’s the step-by-step.

  1. Initialization: Your application calls h3_metal_init(). This function selects the default Metal device (typically the integrated GPU on M-series chips), creates a command queue, and compiles the Minimax-H3 Metal shader from embedded source code into a MTLComputePipelineState object. This happens once, upfront.

  2. Buffer allocation: When you’re ready to hash, you call an allocation function that creates two MTLBuffer objects—one for input keys (packed as byte sequences) and one for output hashes (as 64-bit integers). Because of unified memory, these buffers are visible to both CPU and GPU without explicit synchronization in many cases.

  3. Dispatch: The library encodes a compute command into the command queue. It sets the input and output buffers as arguments to the kernel, calculates the threadgroup size and grid size based on the number of keys, and commits the command buffer.

  4. GPU execution: The Metal shader kernel—a direct translation of the Minimax-H3 algorithm into MSL—runs across hundreds or thousands of GPU threads. Each thread processes one or more keys, computing the hash entirely in registers and writing the result to the output buffer.

  5. Completion: The library optionally waits for the GPU to finish (or you can use a completion handler for async workflows). The output buffer now contains your hashes, readable directly from CPU code with zero copy overhead.

The elegance here is what’s missing. There’s no memory mapping dance, no separate GPU memory pool, no serialization format. The input is raw bytes; the output is raw uint64s. This is what “native” actually means.

Getting Started: A Practical Walkthrough

You need a Mac with Apple Silicon (M1 or later) and Xcode Command Line Tools installed. That’s it.

Step 1: Clone and build.

git clone https://github.com/antirez/h3.c.git
cd h3.c
make

This produces a static library (libh3-metal.a) and a test executable.

Step 2: Link into your project. If you’re using a Makefile-based C project:

CFLAGS += -framework Metal -framework Foundation
LDFLAGS += -L/path/to/h3.c -lh3-metal

For Swift or Objective-C projects in Xcode, add the library to your target’s “Link Binary With Libraries” build phase and add the Metal and Foundation frameworks.

Step 3: Hash some strings.

#include "h3-metal.h"

int main() {
    h3_metal_context *ctx = h3_metal_init();
    if (!ctx) {
        fprintf(stderr, "Failed to initialize Metal context\n");
        return 1;
    }

    const char *keys[] = {"hello", "world", "h3-metal"};
    uint64_t hashes[3];

    // The library provides a convenience function for small batches
    h3_metal_hash_strings(ctx, keys, 3, hashes);

    for (int i = 0; i < 3; i++) {
        printf("%s -> %llu\n", keys[i], hashes[i]);
    }

    h3_metal_free(ctx);
    return 0;
}

For larger workloads, you’ll want to use the batch API directly. Pack your keys contiguously in memory with length prefixes, pass the buffer to the GPU kernel, and retrieve hashes in one shot. The test file in the repository demonstrates this pattern.

Step 4: Measure. The included benchmark hashes millions of keys and reports throughput. On an M2 Max, expect throughput in the tens of millions of hashes per second for large batches. The exact number depends on key length distribution and batch size, but the ceiling is high enough that hashing becomes a rounding error in most pipelines.

Performance and Benchmarks: What to Expect

Let’s talk numbers—but with context. Raw throughput benchmarks without workload characterization are marketing, not engineering.

Batch size matters. GPU kernels have launch overhead. For fewer than ~10,000 keys, the CPU scalar implementation will likely be faster because the GPU dispatch latency dominates. The crossover point depends on your chip generation, but as a rule of thumb: if you’re hashing fewer than 50,000 items, benchmark both paths. Above 100,000 keys, the GPU wins decisively.

Key length distribution. Minimax-H3’s performance is largely independent of key length for short strings (under ~64 bytes) because the algorithm processes data in fixed-size chunks with minimal branching. For very long keys, memory bandwidth becomes the bottleneck, and the GPU’s advantage over CPU narrows—both are waiting on the same unified memory subsystem.

Concurrent workloads. The library doesn’t manage multiple command queues or handle concurrent access internally. If you’re calling it from multiple threads, you need your own synchronization. For server-side use cases, consider one context per thread or a context pool.

Real-world throughput. On an M1 Pro with a batch of 10 million 32-byte keys, you can expect roughly 500-800 million hashes per second. That’s fast enough to hash every word in the Common Crawl in under a minute. Your actual mileage will vary, but the order of magnitude is clear: this is not a bottleneck you need to optimize around.

A Balanced Take: Strengths and Limitations

No technology is a silver bullet. Here’s an honest assessment.

Strengths:

  • Zero-dependency native performance. No Python, no ML runtime, no Docker. A single C file and a Metal shader.
  • Unified memory advantage. Apple Silicon’s architecture eliminates the host-to-device copy that plagues discrete GPU programming. Data flows at memory bandwidth speeds.
  • Educational value. The codebase is small enough to read in an afternoon. If you want to understand how to write Metal compute kernels and wrap them in a C API, this is a perfect case study.
  • Production readiness. The Minimax-H3 algorithm itself has been vetted for collision resistance and distribution quality. The implementation is clean and well-structured.

Limitations:

  • Apple Silicon only. No Intel Mac support, no iOS support (yet—the Metal code would work, but the build system and API assume macOS). No Linux, no Windows. This is a deliberate trade-off, not an oversight.
  • Single-algorithm scope. H3-metal hashes strings with Minimax-H3. If you need SHA-256, BLAKE3, or a different hash, you’re writing your own Metal kernel. The library doesn’t generalize.
  • No streaming API. The current interface assumes you have all keys available upfront. For incremental hashing or stream processing, you’ll need to batch or adapt the code.
  • Limited documentation. The README covers the basics, but you’ll need to read the source to understand edge cases, error handling, and advanced usage patterns.

How FDEs Can Leverage This Today

Forward Deployed Engineers sit at the intersection of customer problems and engineering constraints. H3-metal is relevant in several scenarios that come up repeatedly in FDE work.

Deduplication at the edge. Imagine a customer with a 50TB dataset of log files that need deduplication before ingestion. Moving that data to a cloud hashing service would take hours and cost a fortune. An M2 MacBook Pro running H3-metal can compute hashes locally at line rate, identify duplicates, and only ship the unique records. This is the kind of pragmatic optimization that makes FDEs invaluable—solving the problem where the data lives, not where the cloud provider wants it.

Feature engineering pipelines. When building ML features that involve hashing categorical variables (user IDs, session tokens, URL paths), the hashing step is often a silent bottleneck in ETL pipelines. H3-metal can be embedded directly in a preprocessing stage, running on the same machine that handles data validation and transformation. No need to spin up a Spark cluster just to hash strings.

Prototyping on-device ML. The pattern H3-metal demonstrates—Metal shaders for compute, C API for integration—is directly applicable to custom ML operators. If you’re prototyping a model that needs a novel activation function or a custom embedding lookup, you can follow this exact architecture. Write the kernel in MSL, wrap it in a minimal C library, and benchmark against Core ML. For many simple operators, the hand-rolled Metal approach will be faster and more flexible.

If you’re building these kinds of solutions, the ability to move fast from idea to working prototype is what separates effective FDEs from the rest. For a deeper dive into that methodology, check out How FDEs Turn a Messy Customer Problem into a Shipped Prototype in 7 Days.

Learning the GPU programming model. Metal Shading Language is C++14-based and approachable for anyone with systems programming experience. H3-metal is a compact, real-world example that’s easier to learn from than Apple’s sample code (which tends to be either trivial or over-engineered). If GPU programming is on your skill development roadmap, this library is a great starting point. The pattern of reading source code to master complex technical topics is something we explore in depth in A Working Engineer’s Pattern for Using LLMs to Learn Complex Technical Topics.

Building customer-facing tools. When you need to explain to a customer’s engineering team why your hashing approach is fast and reliable, having a clean, well-structured codebase to reference is invaluable. The documentation skills required for this kind of technical communication are covered in Writing Customer-Facing Technical Docs That Actually Get Read by Stakeholders.

FAQ

Q: Is H3-metal suitable for cryptographic use cases?

No. Minimax-H3 is a non-cryptographic hash function. It’s designed for speed and collision resistance in data structures like hash tables and Bloom filters. It offers no protection against preimage attacks, second preimage attacks, or collision attacks by a determined adversary. Use SHA-256 or BLAKE3 for security-sensitive applications.

Q: Can I use this on an iPhone or iPad?

The Metal shader code is compatible with iOS and iPadOS devices that have Apple Silicon (A-series chips with Apple GPUs). However, the current build system and API are macOS-specific. Porting to iOS would require creating an Xcode project, handling the different Metal device selection API, and potentially adjusting buffer allocation patterns for the more constrained memory environment. It’s entirely feasible but not turnkey.

Q: How does this compare to xxHash or CityHash on CPU?

For small batches (under ~10,000 keys), a well-optimized CPU implementation of xxHash will be faster due to GPU dispatch overhead. For large batches, H3-metal will significantly outperform any CPU hash function because it parallelizes across hundreds of GPU cores. The exact crossover point depends on your hardware and workload, but it’s typically in the 50,000-100,000 key range.

Q: Does this work with multiple GPUs (e.g., M2 Ultra)?

The M2 Ultra presents as a single Metal device with a larger GPU. H3-metal will use it transparently. For multi-GPU setups like the Mac Pro with discrete MPX modules, the library currently selects the default device only. You’d need to modify the initialization code to enumerate devices and distribute work manually.

Q: What’s the memory overhead per hash?

The output is 8 bytes per hash (uint64_t). The input buffer size depends on your key encoding. If you’re hashing fixed-length keys, the input buffer is num_keys * key_length bytes. If variable-length, you’ll need an offset or length array alongside the packed key data. The GPU kernel itself uses a small, fixed amount of per-thread memory (registers and threadgroup memory) that doesn’t scale with batch size.

Q: Can I contribute or fork this for my own hash function?

Absolutely. The repository is open source. The architecture cleanly separates the Metal shader (in h3.metal) from the C wrapper (in h3-metal.c). To adapt it for a different hash function, replace the kernel code in the .metal file while keeping the buffer layout and dispatch logic intact. The C API can remain unchanged if your new hash also produces a uint64_t output.

#apple-silicon#llm-inference#metal#on-device-ai

Want to build like a Forward Deployed Engineer?

FDE Coach is a cohort-based program in frontend, backend, AWS, and AI. Build real products and get referred to 200+ hiring partners.

Explore the program

More ai news

August 15 · 0d left
Enroll Now