Skip to content
>sdcb.ai
2.0 releasedlatest v2.0.0 · Oct 12, 2026100% C#Apache-2.0

HyMT2Sharp

Pure-C# Hunyuan translation, now on your GPU. No llama.cpp — reference one NuGet package and the weights come with it.

Install
dotnet add package Sdcb.HyMT2Sharp
dotnet add package Sdcb.GgufWeights.Hy-MT2-1.8B-2Bit

Line two is the 2Bit weights (~573 MB). Passing a GGUF file path still works.

15.8k
tok/s prefill · RTX 3080 Ti Vulkan
+4–18%
prefill vs llama.cpp CUDA, same box
5 / 5
official quants verified on CPU and GPU
116
commits and 31 PRs from 1.0 to 2.0

Translation race

One sentence, four engines, replayed at measured speed

Each lane eats 83 prompt tokens at its prefill speed, then emits 60 tokens at its decode speed. Speeds are measured on a Ryzen 7 5800X + RTX 3080 Ti (prefill 512 / decode 128) — the model is not running in your browser.

prompt

SimdPaddleOCR is officially released today (NuGet: Sdcb.SimdPaddleOCR). It is a complete OCR inference engine written entirely in C#. It does not depend on Paddle Inference or ONNX Runtime, and it does not require shipping OpenCV native libraries.

HyMT2SharpVulkan · 3080 Ti—
llama.cppCUDA · 3080 Ti—
HyMT2SharpCPU · 5800X—
llama.cppCPU · 5800X—

prefilldecodeSwitch to Q4_K_M+ for a head-to-head — we still lose GPU decode.

2Bit

Q2_0C
Weights
573 MB
NuGet parts
3
Vulkan pp / tg
15.8k / 299
CPU pp / tg
489 / 47.53

A Tencent-specific format: llama.cpp loads it on neither CPU nor GPU, so HyMT2Sharp Vulkan is the only GPU path today. Recommended: just over half the size of Q4_K_M, fastest decode.

NuGet
dotnet add package Sdcb.HyMT2Sharp
dotnet add package Sdcb.GgufWeights.Hy-MT2-1.8B-2Bit
Program.cs
using Sdcb.GgufWeights.Hy_MT2_1_8B_2Bit;
using Sdcb.HyMT2Sharp.Backends;
using Sdcb.HyMT2Sharp.Model;

// Metal → Vulkan → CPU
using HunyuanDenseModel model = new(Weights.Model(), backend: BackendFactory.Create());

Backends

The fastest backend, picked for you

BackendFactory.Create() tries Metal → Vulkan → CPU; pass "cpu" / "vulkan" / "metal" or set HYMT_BACKEND to force one. CPU kernels then step down by instruction set.

Vulkan / Metal interop, scheduling and memory management are all C# — the repo is still 100% C#.

GPU
  1. Metal
    macOS · Apple Silicon
    M4 VM ~1,300 / ~70
  2. Vulkan
    Windows · Linux · needs a driver
    3080 Ti 15.8k / 299
  3. CPU
    returns null when neither is available
    see below
CPU
  1. AVX-512
    Xeon 8559C
    ~23% faster prefill than AVX2
  2. AVX-VNNI
    vpdpbusd
    Q8 dot products via VNNI
  3. AVX2
    Ryzen 7 5800X
    2Bit 489 / 47.5
  4. ARM64 SDOT
    M4 VM
    Q4 prefill 147.7 vs llama.cpp 84.6
  5. Vector<T>
    fallback
    BLIS-style GEMM

Intel Arc B580: 2Bit 11,039 / 243 tok/s; Q4_K_M 10,620 / 208 vs llama.cpp Vulkan 7,345 / 194 on the same card. AMD RDNA gets default tile tuning.

One fix worth its own section

966,543 of 966,544 blocks are subnormal

The d / dmin scales in GGUF blocks are fp16, and in real weights they are almost all fp16 subnormals. The old fp16 → fp32 path produced an fp32 subnormal on the way, paying ~150 cycles of microcode assist per conversion.

Random synthetic weights almost never land in the subnormal range, so micro-benchmarks never showed it. Computing on the integer mantissa gives bit-identical results.

decode · Xeon Platinum 8559C

tok/s · longer is better

beforeafter
Q6_K+69%
17.7
30.0
Q4_K_M+23%
26.4
32.6
2Bit+22%
52.1
63.5

Samples

CLI, Server, WinForms, LINQPad — 2Bit and auto GPU by default

WinForms
WinForms

Engine on "Auto" picks Vulkan on a 3080 Ti: 60 decode tokens in 211 ms, about 284 tok/s.

Server
Server

OpenAI-compatible /v1/chat/completions with SSE streaming and a chat page.

LINQPad
LINQPad

The package ships hello-world, cli and minimal-api scripts — they show up in the Samples panel.

Two pitfalls

Found while writing the samples

A stray � in streaming output

The 2Bit model likes to split one multi-byte character into several byte tokens. Decoding the first one alone yields half a UTF-8 sequence, so a � flashes in the stream even though the final text is right.

tokenbeforeafterE3 81…ご�…ご (held)96…ござ…ござ
Generate
string next = model.Tokenizer
    .DecodeVisible(generated)
    .TrimEnd('\uFFFD');

Vulkan on NVIDIA does not give VRAM back

Dispose only called vkDestroyDevice, assuming it would reclaim everything the device owned. The NVIDIA driver does not — each reload leaked ~4.3 GB. Buffers, pipelines, shader modules, fences, descriptor and command pools are now tracked and freed one by one.

before
ErrorOutOfDeviceMemory
after
One bar per reload; dashed line = 12 GB on a 3080 Ti

Upgrading from 1.0

Three packages became one

The namespace is still Sdcb.HyMT2Sharp.Model; new HunyuanDenseModel(path) and Forward are unchanged, and omitting backend behaves exactly like 1.0.

Upgrade
# 1.x
dotnet remove package Sdcb.HyMT2Sharp.Gguf
dotnet remove package Sdcb.HyMT2Sharp.Kernels
dotnet remove package Sdcb.HyMT2Sharp.Model
# 2.0
dotnet add package Sdcb.HyMT2Sharp
  • KvCacheConfig: None / Memory / BlockPolicy (prefix block pool)
  • bf16 KV cache (half the memory); chunked prefill
  • Sampler: temperature / top-k / top-p / min-p / repetition penalty
  • Load GGUF from any seekable Stream

Known limitations

Not there yet

  • Vulkan decode is 6–18% slower than llama.cpp CUDA, whose int8 dot-product GEMV sits closer to the bandwidth limit.
  • GPU load keeps an extra fp16 copy for prefill GEMM — about 4.3 GB VRAM per instance on a 3080 Ti.
  • Metal is only measured in an M4 VM; real hardware should do better.
  • HunyuanDenseModel is not thread-safe — queue requests or use one instance each.

Offline translation in one package

Open source under Apache-2.0 — a star is always appreciated. Chat with us in QQ group 495782587 (.NET骚操作).

Related writing