{
    "archive_path": "archive/1790373923.317386",
    "base_url": "go.dev/blog/simd-experiment",
    "basename": "simd-experiment",
    "bookmarked_date": "2026-09-25 22:05",
    "canonical": {
        "archive_org_path": "https://web.archive.org/web/go.dev/blog/simd-experiment",
        "dom_path": "output.html",
        "favicon_path": "favicon.ico",
        "git_path": "git/",
        "google_favicon_path": "https://www.google.com/s2/favicons?domain=go.dev",
        "headers_path": "headers.json",
        "htmltotext_path": "htmltotext.txt",
        "index_path": "index.html",
        "media_path": "media/",
        "mercury_path": "mercury/content.html",
        "pdf_path": "output.pdf",
        "readability_path": "readability/content.html",
        "screenshot_path": "screenshot.png",
        "singlefile_path": "singlefile.html",
        "warc_path": "warc/",
        "wget_path": "go.dev/blog/simd-experiment.html"
    },
    "domain": "go.dev",
    "extension": "",
    "hash": "1Y6M136A6Q93B36BG9ZS",
    "history": {
        "archive_org": [],
        "dom": [],
        "favicon": [
            {
                "cmd": [
                    "curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--max-time",
                    "120",
                    "--output",
                    "favicon.ico",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.4 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.14.1 (x86_64-pc-linux-gnu)",
                    "https://www.google.com/s2/favicons?domain=go.dev"
                ],
                "cmd_version": "curl 8.14.1 (x86_64-pc-linux-gnu)",
                "end_ts": "2026-09-25T22:06:41.610147+00:00",
                "index_texts": null,
                "output": "favicon.ico",
                "pwd": "/data/archive/1790373923.317386",
                "schema": "ArchiveResult",
                "start_ts": "2026-09-25T22:06:41.125239+00:00",
                "status": "succeeded"
            }
        ],
        "git": [],
        "headers": [
            {
                "cmd": [
                    "curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--head",
                    "--max-time",
                    "120",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.4 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.14.1 (x86_64-pc-linux-gnu)",
                    "https://go.dev/blog/simd-experiment"
                ],
                "cmd_version": "curl 8.14.1 (x86_64-pc-linux-gnu)",
                "end_ts": "2026-09-25T22:06:42.265794+00:00",
                "index_texts": null,
                "output": "headers.json",
                "pwd": "/data/archive/1790373923.317386",
                "schema": "ArchiveResult",
                "start_ts": "2026-09-25T22:06:41.614763+00:00",
                "status": "succeeded"
            }
        ],
        "htmltotext": [],
        "media": [],
        "mercury": [
            {
                "cmd": [
                    "/app/node_modules/@postlight/parser/cli.js",
                    "https://go.dev/blog/simd-experiment"
                ],
                "cmd_version": "1.0.0",
                "end_ts": "2026-09-25T22:06:55.890068+00:00",
                "index_texts": null,
                "output": "mercury",
                "pwd": "/data/archive/1790373923.317386",
                "schema": "ArchiveResult",
                "start_ts": "2026-09-25T22:06:51.394621+00:00",
                "status": "succeeded"
            }
        ],
        "pdf": [],
        "readability": [
            {
                "cmd": [
                    "/app/node_modules/readability-extractor/readability-extractor",
                    "/tmp/tmpiz0kcc7x",
                    "https://go.dev/blog/simd-experiment"
                ],
                "cmd_version": "0.0.11",
                "end_ts": "2026-09-25T22:06:51.387740+00:00",
                "index_texts": [
                    "Go 1.26 and 1.27 include experimental APIs for Single Instruction Multiple Data (SIMD) operations. SIMD is a native feature of many modern CPUs that allows software to perform uniform operations across vectors of data very quickly, such as adding 8 pairs of float64 values in a single instruction. It can significantly speed up many computationally-intensive tasks, ranging from cryptography to data processing to AI. In fact, Go\u2019s Green Tea garbage collector even makes use of SIMD to accelerate scanning memory for live objects.\nPrior to these new experimental APIs, the only way to access this functionality from Go was by writing Go assembly. This was only worth it for truly performance-critical compute kernels, which meant plenty of software that could benefit from SIMD simply left a lot of the CPU unused.\nGo 1.26 introduced a SIMD API for amd64, and Go 1.27 added APIs for arm64 (specifically NEON) and wasm. However, a basic challenge for a SIMD API is the enormous variation between platforms, not simply in what operations they support, but even in how vectors are represented. Some platforms provide fixed-size vectors, typically between 128 bits and 512 bits, while on others the vector size isn\u2019t known at build time and must be queried when the program starts. To provide full access to the breadth of these platforms, these APIs live in an architecture-dependent archsimd package.\nBut Go 1.27 goes beyond these architecture-dependent APIs and introduces an experimental, fully portable, platform- and size-agnostic SIMD interface,\nloosely based on Highway for C++.  The goal is to support write-once near-asm-performance \u201csimd\u201d code on platforms with SIMD support, and to provide a competent emulation on those platforms that do not (yet) have SIMD support.  The simd package currently supports AVX, AVX2, and AVX512 on amd64, NEON on arm64, and wasm\u2019s SIMD instructions.\nMotivation: variation among SIMD architectures\nSIMD architectures vary in several dimensions.  Some provide a single fixed vector size (wasm, PowerPC, and s390x, 128 bits).  Some provide several fixed vector sizes (amd64, with 128, 256, and 512; loong64 with 128 and 256).  Riscv64 supports vectors of unspecified size between 128 and 65536 bits, though the length is limited to powers of 2.  Arm64 supports one fixed size (128 bits, NEON), and one variable size (128-2048 bits, powers of two only, SVE).  On a given instance of a particular architecture, determining what sizes that particular instance happens to support requires feature checks: amd64, but is it AVX, AVX2, or AVX512?  Arm64, but is it NEON or SVE?  If SVE, how large? Which variant of SVE: SVE, SVE2, or SVE2.1?\nDifferent SIMD architectures vary in how they handle vector masking. For vectors, if-then-else across a vector can be implemented with masks; do the operation, but only assign the result (or load, or store) where the mask is \u201ctrue\u201d.  Some SIMD variants do not provide masks; all operations work across all elements, and \u201cmasking\u201d is done with vector bitmasks and vector boolean operations (wasm, AVX, AVX2, NEON).  Some provide special mask registers, with one bit governing operations on one vector element (AVX512 and RVV).  Others (SVE) allocate one bit per vector byte, but the least-significant bit of each element\u2019s mask bits governs masked operations.  AVX2 also supports masked loads and stores, but using a plain vector as the mask, and with the most-significant bit governing the operation.\nA third source of variation is in the operations themselves.  Each architecture provides its own primitives for rearranging vector elements; some require constant inputs, others support variable inputs.  Different SIMD architectures support different crypto-related operations.  Even basic arithmetic can have varying support; for example wasm lacks comparisons for vectors of 64-bit integers.  Even for a given vector length on a particular architecture, instruction support depends on \u201cfeatures\u201d that must be checked.\nEven though Go\u2019s architecture-dependent archsimd package was designed to be as uniform as possible across architectures, many of these quirks remain, and make designing, writing, and testing code for multiplatform SIMD onerous.  We could do more in the archsimd package to make the different architectures appear more similar, but we can only go so far without compromising efficiency.\nOverview\nThe new simd package hides these differences by removing fixed-size vectors from the type system, and by only supporting those operations that are in the intersection of all the different platforms, and fills gaps in the intersection with efficient emulation in terms of other SIMD instructions.  The goal is a set of operations that is\n\nadequate to support many data processing algorithms that benefit from a vectorized implementation (but are not tied to a particular vector size),\nis as efficient as assembly language when the source code operations match the underlying hardware,\nis otherwise emulated as well as possible,\nand is easy to read and understand (even/especially if an LLM ends up writing the code).\n\nOn platforms that lack SIMD instructions or that lack support in archsimd, all of the operations are emulated,\nso that code written using the simd package will always run.\nTo use this experimental package, set GOEXPERIMENT=simd at build time, just like using the experimental archsimd package.\nThe simd vector types are just capitalized, plural, primitive types, for example simd.Uint8s or simd.Float32s. Vectors are loaded from and stored to slices, for example:\n// innerProduct returns the inner product of x and y.\nfunc innerProduct(x, y []float32) float32 {\n    var a simd.Float32s\n    var i int\n    for i = 0; i < len(x)-a.Len()+1; i += a.Len() {\n        u := simd.LoadFloat32s(x[i : i+a.Len()])\n        v := simd.LoadFloat32s(y[i : i+a.Len()])\n        a = u.MulAdd(v, a)\n    }\n    if i < len(x) {\n        u, _ := simd.LoadFloat32sPart(x[i:])\n        v, _ := simd.LoadFloat32sPart(y[i:])\n        a = u.MulAdd(v, a)\n    }\n    return sum(a)\n}\n// sum returns scalar sum of elements of x.\nfunc sum(x simd.Float32s) float32 {\n    s := make([]float32, x.Len())\n    x.Store(s)\n    var r float32\n    for _, e := range s {\n        r += e\n    }\n    return r\n}\n\nThis example also shows one of the limitations of the first experimental release of this package; because there\u2019s no common way to sum across all the elements of a vector, it\u2019s not supported by simd in Go 1.27, though ReduceSum will appear in the next release so sum can be replaced with just simd.ReduceSum.\nSIMD comparisons produce mask values, which are specific to the corresponding vector element width, so that comparisons of Int8s produce Mask8s, etc., and mask values can be used to select and filter vectors.\nSupported simd package operations as of Go 1.27\nIn this table, V and U are vector types, M is a mask type, E is a scalar type, and W is a width.\nPackage-Level Load / Broadcast Functions\n\n\n\n\nFunction\nInt8s\nInt16s\nInt32s\nInt64s\nUint8s\nUint16s\nUint32s\nUint64s\nFloat32s\nFloat64s\n\n\n\n\nLoadV([]E) V\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\nLoadVPart([]E) (V, int)\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\nBroadcastV(E) V\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\n\nStore/String operations\n\n\n\n(x\u202fV).Method(...)\nInt8s\nInt16s\nInt32s\nInt64s\nUint8s\nUint16s\nUint32s\nUint64s\nFloat32s\nFloat64s\n\n\n\n\nStore(s []E)\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\nStorePart(s []E) int\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\nString() string\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\n\nArithmetic operations\n\n\n\n(x\u202fV).Method(...)\u202fV\nInt8s\nInt16s\nInt32s\nInt64s\nUint8s\nUint16s\nUint32s\nUint64s\nFloat32s\nFloat64s\n\n\n\n\nAbs() V\nY\nY\nY\n\n\n\n\n\nY\nY\n\n\nAdd(y V) V\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\nAddSaturated(y V) V\nY\nY\n\n\nY\nY\n\n\n\n\n\n\nAverage(y V) V\n\n\n\n\nY\nY\n\n\n\n\n\n\nDiv(y V) V\n\n\n\n\n\n\n\n\nY\nY\n\n\nIfElse(mask MaskWs, y V) V\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\nLen() int\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\nMasked(mask MaskWs) V\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\nMax(y V) V\nY\nY\nY\n\nY\nY\nY\n\nY\nY\n\n\nMin(y V) V\nY\nY\nY\n\nY\nY\nY\n\nY\nY\n\n\nMul(y V) V\nY\nY\nY\n\nY\nY\nY\n\nY\nY\n\n\nMulAdd(y V, z V) V\n\n\n\n\n\n\n\n\nY\nY\n\n\nNeg() V\nY\nY\nY\nY\n\n\n\n\nY\nY\n\n\nNot() V\nY\nY\nY\nY\nY\nY\nY\nY\n\n\n\n\nOr(y V) V\nY\nY\nY\nY\nY\nY\nY\nY\n\n\n\n\nSqrt() V\n\n\n\n\n\n\n\n\nY\nY\n\n\nSub(y V) V\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\nSubSaturated(y V) V\nY\nY\n\n\nY\nY\n\n\n\n\n\n\nXor(y V) V\nY\nY\nY\nY\nY\nY\nY\nY\n\n\n\n\n\nBoolean and vector masking operations\n\n\n\n(x\u202fV).Method(...)\u202fV\nInt8s\nInt16s\nInt32s\nInt64s\nUint8s\nUint16s\nUint32s\nUint64s\nFloat32s\nFloat64s\n\n\n\n\nAnd(y V) V\nY\nY\nY\nY\nY\nY\nY\nY\n\n\n\n\nAndNot(y V) V\nY\nY\nY\nY\nY\nY\nY\nY\n\n\n\n\nCarrylessMultiplyEven(y V) V\n\n\n\n\n\n\n\nY\n\n\n\n\nCarrylessMultiplyOdd(y V) V\n\n\n\n\n\n\n\nY\n\n\n\n\nIfElse(mask MaskWs, y V) V\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\nMasked(mask MaskWs) V\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\nNot() V\nY\nY\nY\nY\nY\nY\nY\nY\n\n\n\n\nOr(y V) V\nY\nY\nY\nY\nY\nY\nY\nY\n\n\n\n\nXor(y V) V\nY\nY\nY\nY\nY\nY\nY\nY\n\n\n\n\n\nComparison operations\n\n\n\n(x\u202fV).Method(...)\u202fM\nInt8s\nInt16s\nInt32s\nInt64s\nUint8s\nUint16s\nUint32s\nUint64s\nFloat32s\nFloat64s\n\n\n\n\nEqual(y V) MaskWs\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\nGreater(y V) MaskWs\nY\nY\nY\nY\n\nY\nY\nY\nY\nY\n\n\nGreaterEqual(y V) MaskWs\nY\nY\nY\nY\n\nY\nY\nY\nY\nY\n\n\nLess(y V) MaskWs\nY\nY\nY\nY\n\nY\nY\nY\nY\nY\n\n\nLessEqual(y V) MaskWs\nY\nY\nY\nY\n\nY\nY\nY\nY\nY\n\n\nNotEqual(y V) MaskWs\nY\nY\nY\nY\nY\nY\nY\nY\nY\nY\n\n\n\nConversion operations\n\n\n\n(x\u202fV).Method(...)\u202fU\nInt8s\nInt16s\nInt32s\nInt64s\nUint8s\nUint16s\nUint32s\nUint64s\nFloat32s\nFloat64s\n\n\n\n\nConvertToFloatW() FloatWs\n\n\nY\n\n\n\n\n\n\n\n\n\nConvertToIntW() IntWs\n\n\n\n\nY\nY\nY\nY\nY\n\n\n\nConvertToUintW() UintWs\nY\nY\nY\nY\n\n\n\n\n\n\n\n\nToMask() (to MaskWs)\nY\nY\nY\nY\n\n\n\n\n\n\n\n\n\nMask Methods\n\n\n\n(m\u202fM).Method(...)\u202fM)\nMask8s\nMask16s\nMask32s\nMask64s\n\n\n\n\nAnd(y M) M\nY\nY\nY\nY\n\n\nOr(y V) V\nY\nY\nY\nY\n\n\nString() string\nY\nY\nY\nY\n\n\nToIntWs() (to IntWs)\nY\nY\nY\nY\n\n\n\nShift and rotate operations\n\n\n\n(x\u202fV).Method()\u202fV\nInt8s\nInt16s\nInt32s\nInt64s\nUint8s\nUint16s\nUint32s\nUint64s\nFloat32s\nFloat64s\n\n\n\n\nRotateAllLeft(dist uint64) V\n\nY\nY\nY\n\nY\nY\nY\n\n\n\n\nRotateAllRight(dist uint64) V\n\nY\nY\nY\n\nY\nY\nY\n\n\n\n\nShiftAllLeft(dist uint64) V\n\nY\nY\nY\n\nY\nY\nY\n\n\n\n\nShiftAllRight(dist uint64) V\n\nY\nY\n\n\nY\nY\nY\n\n\n\n\n\nZero-cost reshaping operations\n\n\n\n(x\u202fV).Method(...)\u202fU\nInt8s\nInt16s\nInt32s\nInt64s\nUint8s\nUint16s\nUint32s\nUint64s\nFloat32s\nFloat64s\n\n\n\n\nToBits() UintWs\nY\nY\nY\nY\n\n\n\n\nY\nY\n\n\nReshapeToUint8s() Uint8s\n\n\n\n\n\nY\nY\nY\n\n\n\n\nReshapeToUint16s() Uint16s\n\n\n\n\nY\n\nY\nY\n\n\n\n\nReshapeToUint32s() Uint32s\n\n\n\n\nY\nY\n\nY\n\n\n\n\nReshapeToUint64s() Uint64s\n\n\n\n\nY\nY\nY\n\n\n\n\n\nBitsToFloatW() FloatWs\n\n\n\n\n\n\nY\nY\n\n\n\n\nBitsToIntW() IntWs\n\n\n\n\nY\nY\nY\nY\n\n\n\n\n\nTransition to/from platform-specific code\nIt may happen that the simd package is too limited for all parts of a particular application, or that we have not yet provided an adequate emulation for some necessary feature.  For that case, the simd package supports transition to and from architecture-specific SIMD.  Each vector type in the simd package has a conversion method ToArch() returning an any.  That any can be type-asserted to one of the architecture-specific types for a platform.  To convert back, use one of the simd.<SimdType>FromArch functions.  For portable code this creates an obligation to write architecture-specific code for each of the platforms, including an emulation.\nHere\u2019s a complete example for a method/function that is currently missing, but should be added in Go 1.28. Suppose your algorithm needs Int8s.OnesCount() (which simd in Go 1.27 lacks).  Rather than rewriting the entire algorithm for each platform, it\u2019s possible to just implement the missing operation.\nFirst, for amd64, which lacks the instruction for AVX and AVX2, but not AVX512:\n//go:build goexperiment.simd && amd64\npackage simd_test\nimport (\n    \"simd\"\n    \"simd/archsimd\"\n)\nvar popcnt4x16 = [16]int8{0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}\nvar popcnt4x32 = [32]int8{\n    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,\n    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,\n}\n// OnesCount returns the number of one bits for each element.\nfunc OnesCount(v simd.Int8s) simd.Int8s {\n    switch x := v.ToArch().(type) {\n    case archsimd.Int8x16:\n        lut := archsimd.LoadInt8x16Array(&popcnt4x16)\n        mask0f := archsimd.BroadcastInt8x16(0x0f)\n        lo := x.And(mask0f)\n        hi := x.ToBits().ReshapeToUint16s().ShiftAllRight(4).\n                ReshapeToUint8s().BitsToInt8().And(mask0f)\n        return simd.Int8sFromArch(lut.PermuteOrZero(lo).\n                Add(lut.PermuteOrZero(hi)))\n    case archsimd.Int8x32:\n        lut := archsimd.LoadInt8x32Array(&popcnt4x32)\n        mask0f := archsimd.BroadcastInt8x32(0x0f)\n        lo := x.And(mask0f)\n        hi := x.ToBits().ReshapeToUint16s().ShiftAllRight(4).\n                ReshapeToUint8s().BitsToInt8().And(mask0f)\n        return simd.Int8sFromArch(lut.PermuteOrZeroGrouped(lo).\n                Add(lut.PermuteOrZeroGrouped(hi)))\n    case archsimd.Int8x64:\n        return simd.Int8sFromArch(x.OnesCount())\n    default:\n        // GODEBUG=simd=0 emulation\n        return OnesCountEmulated(v)\n    }\n}\n\nThe interface conversion and type switch look like they should be inefficient, but the compiler-side implementation of simd specializes code and optimizes away the type switch.\nNEON and Wasm both support Int8s.OnesCount(), so their implementation is much simpler, though it still uses Int8s.ToArch and Int8sFromArch.\n//go:build goexperiment.simd && (wasm || arm64)\npackage simd_test\nimport (\n    \"simd\"\n    \"simd/archsimd\"\n)\n// OnesCount returns the number of one bits for each element.\nfunc OnesCount(v simd.Int8s) simd.Int8s {\n    // TODO when SVE is added, this won't work\n    switch x := v.ToArch().(type) {\n    case archsimd.Int8x16:\n        return simd.Int8sFromArch(x.OnesCount())\n    default:\n        // GODEBUG=simd=0 emulation\n        return OnesCountEmulated(v)\n    }\n}\n\nDon\u2019t forget that some people don\u2019t have hardware SIMD support:\n//go:build goexperiment.simd && !(amd64 || wasm || arm64)\n\npackage simd_test\nimport (\n    \"simd\"\n)\n// OnesCount returns the number of one bits for each element.\nfunc OnesCount(v simd.Int8s) simd.Int8s {\n    return OnesCountEmulated(v)\n}\n\nAnd to complete the exercise, a separate emulation function shared as a fallback across all implementations:\n//go:build goexperiment.simd\npackage simd_test\nimport (\n    \"simd\"\n)\n// OnesCountEmulated returns the number of one bits for each element.\nfunc OnesCountEmulated(v simd.Int8s) simd.Int8s {\n    a := [2]uint64{}\n    v.ToBits().ReshapeToUint64s().Store(a[:])\n    a0, a1 := a[0], a[1]\n    m1 := uint64(0x5555555555555555)\n    m2 := uint64(0x3333333333333333)\n    m4 := uint64(0x0f0f0f0f0f0f0f0f)\n    a0 = (a0 & m1) + ((a0 >> 1) & m1)\n    a1 = (a1 & m1) + ((a1 >> 1) & m1)\n    a0 = (a0 & m2) + ((a0 >> 2) & m2)\n    a1 = (a1 & m2) + ((a1 >> 2) & m2)\n    a0 = (a0 & m4) + ((a0 >> 4) & m4)\n    a1 = (a1 & m4) + ((a1 >> 4) & m4)\n    a[0], a[1] = a0, a1\n    return simd.LoadUint64s(a[:]).ReshapeToUint8s().BitsToInt8()\n}\n\nAPI intersection and method emulation\nWhatever operations the simd package offers need to run acceptably well on most architectures.  As a first step, any operation that is supported everywhere, can easily be supported on simd.  This tends to include loads, stores, arithmetic, and comparisons (but not all comparisons!).\nA naive intersection across SIMD methods from different architectures still leaves plenty of holes.\nThese are filled by adding emulations to the various architecture-specific archsimd APIs.\nThese APIs already contain many trivial emulations to simplify life for Go programmers;\nsigned and unsigned integer addition use the same instruction,\nbut in the same way that Go supports the + operator for both int and uint,\nthe archsimd package provides both Int8x16.Add(Int8x16) and Uint8x16.Add(Uint8x16),\neven though those compile to the same instruction.\nModern programming languages also don\u2019t expect programmers to know how to implement floating point negation and absolute value with bit fiddling,\nso archsimd implements that where necessary, or \u201cemulates\u201d if you look at it just so.\nThere are many emulations that require just 2 or 3 instructions; for example, some architectures support only a same-value shift distance across vector elements, while others support a different shift distance for each vector element.  To support scalar shifting in simd, we emulate scalar shift with vector shift.  Some architectures lack some unsigned comparisons\u2013these are just signed comparison, plus two XORs with a constant.\nNot all missing instructions are that simple.  The \u201ccarryless multiply\u201d instruction is important to cryptography and CRC checksumming, but it isn\u2019t always supported.  Leaving that out of the simd API would prevent its use for some important algorithms.  Therefore, we provide an emulation, and because one important use is in crypto, its run time does not vary depending on its inputs.\nIn other cases, rather than implement a primitive instruction like \u201cadd pairs\u201d (also called \u201chorizontal addition\u201d), for the simd package in the next release we will provide the higher level operation that add pairs is usually used for, which is sum reduction.  This also helps insulate users from vector-length dependence; even given the hardware instruction for adding pairs, the number of reduction steps depends on the vector length.\nThe constraint of supporting all platforms, including ones that we predict will appear in archsimd within the next year or so, forces a somewhat conservative approach to which methods we add to simd.  Riscv64, ppc64, s390x, and loong64 all have their own SIMD extensions.\nGODEBUG settings\nOn platforms where there is some hardware support, behavior can be modified with GODEBUG, to make it easier to test simd-using code with various hardware configurations. You can set the GODEBUG environment variable prior to executing your program.\nIn Go 1.27, levels of SIMD support are roughly described by vector length:\n\nGODEBUG=simd=0 means use emulation for SIMD operations even if the hardware support is available.\nGODEBUG=simd=128 means use 128-bit vectors and their features.  If the features aren\u2019t available, panic immediately.\nGODEBUG=simd=256 means use 256-bit vectors and their features, if possible.\nGODEBUG=simd=512 means use 512-bit vectors and their features, if possible.\nGODEBUG=simd=+128 means use 128-bit vectors and their features even if some features are not supported. If unsupported instructions are used, the code will panic, but if they are not it may still run.  An example of this is Raspberry Pi, which supports NEON but lacks PMULL (carryless multiply).\nGODEBUG=simd=+256 means use 256-bit vectors and their features even if some features are not supported. If unsupported instructions are used, the code will panic, but if they are not it may still run.  An example of this is Apple Silicon\u2019s amd64 emulation, which supports AVX2 but not VPCLMULQDQ (again, carryless multiply).\nGODEBUG=simd=+512 means use 512-bit vectors, even if some features are not supported.\n\nImplementation details\nIf you are debugging code that uses simd, or even just look at a stack trace, you will notice some weird extra types and methods.  The reason is that simd is both a package, an internal implementation package, and some AST rewriting in the front end of the compiler.\nThe AST rewrite creates multiple specialized copies of functions, variables, and types that mention simd types, where simd types are replaced with references to size-specialized types in simd/internal/bridge.  Each of these bridge types is defined as an archsimd type, but with a restricted set of methods.  The specialized functions, variables, and types acquire a suffix of the form @simdNNN, where NNN is either a vector length (128, 256, or 512) or 0, indicating emulation.  Functions that mention simd internally, but not in their signature, are converted to wrappers that switch on the SIMD level detected at program start, and call the appropriate specialized version of that function.  Specialized functions call other specialized functions directly without dispatch overhead (and perhaps with inlining).  This rewrite strategy was chosen as a compromise between code duplication and SIMD performance; the overhead is hoisted as high as necessary to avoid dispatch within SIMD computations, but not higher.  If SIMD dispatch appears \u201ctoo low\u201d in a computation, a gratuitous mention of a simd type will move it upwards, as in this example:\nfunc BenchmarkVpsumdSIMD(b *testing.B) {\n    // mention \"simd\" so the benchmark loop calls specialized vpsumd3 directly\n    var _ simd.Uint64s\n    var w, x, y, z uint64 = ... // magic constants omitted.\n    var lo, hi uint64\n    for b.Loop() {\n        // vpsumd3 does simd stuff, but lacks a simd signature,\n        // so that it can be compared with non-SIMD emulations.\n        lo, hi = vpsumd3(w, x, y, z)\n    }\n    sinkLo, sinkHi = lo, hi\n}\n\nWhat\u2019s coming\nWe plan to publish a blog post describing archsimd in greater detail soon.\nFor Go 1.28, we intend to add SVE support to archsimd, and also hope to add that to simd.  More importantly, we hope to add additional SIMD operations to those that the simd package already supports (e.g., OnesCount, mask operations, reduction operations, vector shuffling operations). Go 1.28 will also include a small number of \u201cfeature variants\u201d to avoid downgrading all the way to full emulation for platforms that have a hardware vector implementation but just lack one or a few operations, such as Raspberry Pi."
                ],
                "output": "readability",
                "pwd": "/data/archive/1790373923.317386",
                "schema": "ArchiveResult",
                "start_ts": "2026-09-25T22:06:48.399871+00:00",
                "status": "succeeded"
            }
        ],
        "screenshot": [],
        "singlefile": [],
        "title": [
            {
                "cmd": [
                    "curl",
                    "--silent",
                    "--location",
                    "--compressed",
                    "--max-time",
                    "120",
                    "--user-agent",
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.4 (+https://github.com/ArchiveBox/ArchiveBox/) curl/curl 8.14.1 (x86_64-pc-linux-gnu)",
                    "https://go.dev/blog/simd-experiment"
                ],
                "cmd_version": "curl 8.14.1 (x86_64-pc-linux-gnu)",
                "end_ts": "2026-09-25T22:06:48.395097+00:00",
                "index_texts": null,
                "output": "Platform-independent SIMD in Go - The Go Programming Language",
                "pwd": "/data/archive/1790373923.317386",
                "schema": "ArchiveResult",
                "start_ts": "2026-09-25T22:06:48.353693+00:00",
                "status": "succeeded"
            }
        ],
        "wget": [
            {
                "cmd": [
                    "wget",
                    "--no-verbose",
                    "--adjust-extension",
                    "--convert-links",
                    "--force-directories",
                    "--span-hosts",
                    "--no-parent",
                    "-e",
                    "robots=off",
                    "--reject=js,mjs,woff,woff2,ttf,otf,eot,mp4,m4v,mov,webm,ogv,mkv,avi,flv",
                    "--timeout=120",
                    "--restrict-file-names=windows",
                    "--page-requisites",
                    "--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ArchiveBox/0.7.4 (+https://github.com/ArchiveBox/ArchiveBox/) wget/GNU Wget 1.21.3",
                    "--compression=auto",
                    "--timestamping",
                    "https://go.dev/blog/simd-experiment"
                ],
                "cmd_version": "GNU Wget 1.21.3",
                "end_ts": "2026-09-25T22:06:48.347892+00:00",
                "index_texts": null,
                "output": "go.dev/blog/simd-experiment.html",
                "pwd": "/data/archive/1790373923.317386",
                "schema": "ArchiveResult",
                "start_ts": "2026-09-25T22:06:42.272047+00:00",
                "status": "succeeded"
            }
        ]
    },
    "icons": null,
    "is_archived": true,
    "is_static": false,
    "latest": {
        "archive_org": null,
        "dom": null,
        "favicon": "favicon.ico",
        "git": null,
        "media": null,
        "pdf": null,
        "screenshot": null,
        "singlefile": null,
        "title": "Platform-independent SIMD in Go - The Go Programming Language",
        "warc": null,
        "wget": "go.dev/blog/simd-experiment.html"
    },
    "link_dir": "/data/archive/1790373923.317386",
    "newest_archive_date": "2026-09-25T22:06:51.394621+00:00",
    "num_failures": 0,
    "num_outputs": 6,
    "oldest_archive_date": "2026-09-25T22:06:41.125239+00:00",
    "path": "/blog/simd-experiment",
    "schema": "Link",
    "scheme": "https",
    "snapshot_id": "3043ce58-6233-4e1f-931d-1c67b592ba1e",
    "sources": [
        "/data/sources/1790373923-import.txt"
    ],
    "tags": null,
    "tags_str": "",
    "timestamp": "1790373923.317386",
    "title": "Platform-independent SIMD in Go - The Go Programming Language",
    "updated": "2026-09-25T22:06:41.124812+00:00",
    "updated_date": "2026-09-25 22:06",
    "url": "https://go.dev/blog/simd-experiment"
}