From 8188c294251d66b4dc4c751a62f720533728e978 Mon Sep 17 00:00:00 2001 From: Dmitry Kolesnikov Date: Wed, 28 Feb 2024 00:10:17 +0200 Subject: [PATCH 1/3] initial release --- .github/workflows/build.yml | 48 +++++++++++ .github/workflows/check-test.yml | 34 ++++++++ README.md | 130 +++++++++++++++++++++++++++- go.mod | 8 ++ go.sum | 4 + internal/noasm/cosine.go | 67 +++++++++++++++ internal/noasm/euclidean.go | 47 +++++++++++ internal/pure/cosine.go | 45 ++++++++++ internal/pure/euclidean.go | 32 +++++++ internal/simd/cosine_arm64.go | 13 +++ internal/simd/euclidean_arm64.go | 30 +++++++ internal/simd/euclidean_arm64.s | 42 +++++++++ internal/simd/noasm.go | 20 +++++ types.go | 97 +++++++++++++++++++++ vector_test.go | 141 +++++++++++++++++++++++++++++++ 15 files changed, 756 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/check-test.yml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/noasm/cosine.go create mode 100644 internal/noasm/euclidean.go create mode 100644 internal/pure/cosine.go create mode 100644 internal/pure/euclidean.go create mode 100644 internal/simd/cosine_arm64.go create mode 100644 internal/simd/euclidean_arm64.go create mode 100644 internal/simd/euclidean_arm64.s create mode 100644 internal/simd/noasm.go create mode 100644 types.go create mode 100644 vector_test.go diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..df30005 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,48 @@ +## +## Build the main branch +## +name: build +on: + push: + branches: + - main + - /refs/heads/main + +jobs: + + build: + runs-on: ubuntu-latest + steps: + + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + + - uses: actions/checkout@v4.1.1 + + - name: go build + run: | + go build ./... + + - name: go test + run: | + go test -v -coverprofile=profile.cov $(go list ./... | grep -v /examples/) + + - uses: shogo82148/actions-goveralls@v1 + continue-on-error: true + with: + path-to-profile: profile.cov + + - uses: reecetech/version-increment@2023.10.2 + id: version + with: + scheme: semver + increment: patch + + - name: publish + run: | + git config user.name "GitHub Actions" + git config user.email "github-actions@users.noreply.github.com" + git tag ${{ steps.version.outputs.v-version }} + git push origin -u ${{ steps.version.outputs.v-version }} + diff --git a/.github/workflows/check-test.yml b/.github/workflows/check-test.yml new file mode 100644 index 0000000..5f8119a --- /dev/null +++ b/.github/workflows/check-test.yml @@ -0,0 +1,34 @@ +## +## Unit Tests +## +name: test +on: + pull_request: + types: + - opened + - synchronize + +jobs: + + unit: + runs-on: ubuntu-latest + steps: + + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + + - uses: actions/checkout@v4.1.1 + + - name: go build + run: | + go build ./... + + - name: go test + run: | + go test -v -coverprofile=profile.cov $(go list ./... | grep -v /examples/) + + - uses: shogo82148/actions-goveralls@v1 + continue-on-error: true + with: + path-to-profile: profile.cov diff --git a/README.md b/README.md index 90fe8be..812ca2e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,128 @@ -# vector -Vector Data Type for Golang +# Vector + +The library implement fast (highly optimized) vector algebra for Golang. + +[![Version](https://img.shields.io/github/v/tag/kshard/vector?label=version)](https://github.com/kshard/vector/releases) +[![Documentation](https://pkg.go.dev/badge/github.com/kshard/vector)](https://pkg.go.dev/github.com/kshard/vector) +[![Build Status](https://github.com/kshard/vector/workflows/build/badge.svg)](https://github.com/kshard/vector/actions/) +[![Git Hub](https://img.shields.io/github/last-commit/kshard/vector.svg)](https://github.com/kshard/vector) +[![Coverage Status](https://coveralls.io/repos/github/kshard/vector/badge.svg?branch=main)](https://coveralls.io/github/kshard/vector?branch=main) +[![Go Report Card](https://goreportcard.com/badge/github.com/kshard/vector)](https://goreportcard.com/report/github.com/kshard/vector) + + +## Inspiration + +Hierarchical Navigable Small World (HNSW) algorithms are state-of-the-art techniques employed for approximate nearest neighbor searches. Vectors and the concept of distance are essential components of the algorithm. The algorithm requires large quantity of vector comparisons on the write path. + +The specific count of vector operations performed by HNSW can vary, contingent on factors such as implementation details and the chosen parameters for the algorithm. We have observed a logarithmic progression, starting from 2.2K distance computations per write operation, with subsequent growth reaching 8K and beyond. + +**In handling such extensive data, the significance of nanoseconds cannot be overstated!** + + +## Getting started + +The latest version of the library is available at `main` branch of this repository. All development, including new features and bug fixes, take place on the `main` branch using forking and pull requests as described in contribution guidelines. The stable version is available via Golang modules. + +```go +import "github.com/kshard/vector" + +// Our vectors +a := []float32{60.1699, 24.9384, 24.9384, 60.1699} +b := []float32{59.9311, 30.3609, 30.3609, 59.9311} + +// instantiate Euclidean distance function +euclidean := vector.Euclidean() + +euclidean.Distance(a, b) // 58.921078 + +// instantiate Cosine distance function +cosine := vector.Cosine() + +cosine.Distance(a, b) // 0.001443 +``` + +**Note**: One known limitation, the library requires vectors aligned to 4. + +### Supported CPU architectures + +The library provides two versions of vector algebra functions. The library automatically selects appropriate version depending on the architecture. + +1. A Golang native version optimized for instruction pipelining. +2. A pure assembly implementation leveraging the SIMD ("Single Instruction Multiple Data") instruction set. + +Internally each version is implemented by the package +1. [internal/noasm](internal/noasm/) - optimized Golang version. +2. [internal/simd](internal/simd/) - pure assembly implementation. +3. [internal/pure](internal/pure/) - reference implementation using idiomatic Golang. + +The library provides `Info` function to determine configuration runtime + +```go +config = vector.Info() + +config[vector.CONFIG_XXX] +// XXX_WITH_PURE +// XXX_WITH_NOASM +// XXX_WITH_SIMD +``` + +## Performance + +Instruction pipeline is 3.3x faster and SIMD is 3.8x faster (due to [bounds check elimination](https://go101.org/article/bounds-check-elimination.html)) + +``` +go test -run=^$ -bench=. -cpu=1 + +BenchmarkPureEuclideanF32 4072329 270.5 ns/op +BenchmarkNoAsmEuclideanF32 14938154 81.59 ns/op +BenchmarkSIMDEuclideanF32 16888236 71.15 ns/op +BenchmarkPureCosineF32 3987426 299.8 ns/op +BenchmarkNoAsmCosineF32 11738499 102.2 ns/op +``` + + +## How To Contribute + +The library is [MIT](LICENSE) licensed and accepts contributions via GitHub pull requests: + +1. Fork it +2. Create your feature branch (`git checkout -b my-new-feature`) +3. Commit your changes (`git commit -am 'Added some feature'`) +4. Push to the branch (`git push origin my-new-feature`) +5. Create new Pull Request + +The build and testing process requires [Go](https://golang.org) version 1.13 or later. + +**build** and **test** library. + +```bash +git clone https://github.com/kshard/vector +cd vector +go test +``` + +Checklist: +1. No new bounds check introduced: `go build -gcflags="-d=ssa/check_bce"` +2. No performance degradations: `go test -run=^$ -bench=. -cpu=1` + + +### commit message + +The commit message helps us to write a good release note, speed-up review process. The message should address two question what changed and why. The project follows the template defined by chapter [Contributing to a Project](http://git-scm.com/book/ch5-2.html) of Git book. + +### bugs + +If you experience any issues with the library, please let us know via [GitHub issues](https://github.com/kshard/vectors/issue). We appreciate detailed and accurate reports that help us to identity and replicate the issue. + + +## License + +[![See LICENSE](https://img.shields.io/github/license/kshard/vector.svg?style=for-the-badge)](LICENSE) + + +## References +1. [A Quick Guide to Go's Assembler](https://go.dev/doc/asm) +2. https://github.com/below/HelloSilicon +3. https://github.com/hztools/go-sdr/tree/main/internal/simd +4. [Bounds Check Elimination](https://go101.org/article/bounds-check-elimination.html) + \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..79c23a8 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/kshard/vector + +go 1.22.0 + +require ( + github.com/chewxy/math32 v1.10.1 + golang.org/x/sys v0.17.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..bdca768 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/chewxy/math32 v1.10.1 h1:LFpeY0SLJXeaiej/eIp2L40VYfscTvKh/FSEZ68uMkU= +github.com/chewxy/math32 v1.10.1/go.mod h1:dOB2rcuFrCn6UHrze36WSLVPKtzPMRAQvBvUwkSsLqs= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/internal/noasm/cosine.go b/internal/noasm/cosine.go new file mode 100644 index 0000000..31f5f53 --- /dev/null +++ b/internal/noasm/cosine.go @@ -0,0 +1,67 @@ +// +// Copyright (C) 2024 Dmitry Kolesnikov +// +// This file may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// https://github.com/kshard/vector +// + +package noasm + +import "github.com/chewxy/math32" + +const ENABLED_COSINE = true + +// Cosine distance between two vectors +func CosineF32(a, b []float32) (d float32) { + if len(a) != len(b) { + panic("vectors must have equal lengths") + } + + if len(a)%4 != 0 { + panic("vector length must be multiple of 4") + } + + ab := float32(0.0) + aa := float32(0.0) + bb := float32(0.0) + + for i := 0; i < len(a); i += 4 { + asl := a[i : i+4 : i+4] + bsl := b[i : i+4 : i+4] + + ax0, ax1, ax2, ax3 := asl[0], asl[1], asl[2], asl[3] + bx0, bx1, bx2, bx3 := bsl[0], bsl[1], bsl[2], bsl[3] + + ab0 := ax0 * bx0 + ab1 := ax1 * bx1 + ab2 := ax2 * bx2 + ab3 := ax3 * bx3 + ab += ab0 + ab1 + ab2 + ab3 + + aa0 := ax0 * ax0 + aa1 := ax1 * ax1 + aa2 := ax2 * ax2 + aa3 := ax3 * ax3 + aa += aa0 + aa1 + aa2 + aa3 + + bb0 := bx0 * bx0 + bb1 := bx1 * bx1 + bb2 := bx2 * bx2 + bb3 := bx3 * bx3 + bb += bb0 + bb1 + bb2 + bb3 + } + + s := math32.Sqrt(aa) * math32.Sqrt(bb) + + d = (1 - ab/s) / 2 + + return +} + +// Type Class for Cosine distance +type Cosine int + +func (Cosine) Distance(a, b []float32) float32 { + return CosineF32(a, b) +} diff --git a/internal/noasm/euclidean.go b/internal/noasm/euclidean.go new file mode 100644 index 0000000..9def45d --- /dev/null +++ b/internal/noasm/euclidean.go @@ -0,0 +1,47 @@ +// +// Copyright (C) 2024 Dmitry Kolesnikov +// +// This file may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// https://github.com/kshard/vector +// + +package noasm + +const ENABLED_EUCLIDEAN = true + +// Squared Euclidean distance between two vectors +func EuclideanF32(a, b []float32) (d float32) { + if len(a) != len(b) { + panic("vectors must have equal lengths") + } + + if len(a)%4 != 0 { + panic("vectors length must be multiple of 4") + } + + for i := 0; i < len(a); i += 4 { + av := a[i : i+4 : i+4] + bv := b[i : i+4 : i+4] + x1 := av[0] - bv[0] + x2 := av[1] - bv[1] + x3 := av[2] - bv[2] + x4 := av[3] - bv[3] + + d1 := x1 * x1 + d2 := x2 * x2 + d3 := x3 * x3 + d4 := x4 * x4 + + d += d1 + d2 + d3 + d4 + } + + return +} + +// Type Class for Euclidean distance +type Euclidean int + +func (Euclidean) Distance(a, b []float32) float32 { + return EuclideanF32(a, b) +} diff --git a/internal/pure/cosine.go b/internal/pure/cosine.go new file mode 100644 index 0000000..4150ab4 --- /dev/null +++ b/internal/pure/cosine.go @@ -0,0 +1,45 @@ +// +// Copyright (C) 2024 Dmitry Kolesnikov +// +// This file may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// https://github.com/kshard/vector +// + +package pure + +import ( + "github.com/chewxy/math32" +) + +const ENABLED_COSINE = false + +// Cosine distance between two vectors +func CosineF32(a, b []float32) (d float32) { + if len(a) != len(b) { + panic("vectors must have equal lengths") + } + + ab := float32(0.0) + aa := float32(0.0) + bb := float32(0.0) + + for i := 0; i < len(a); i++ { + ab += a[i] * b[i] + aa += a[i] * a[i] + bb += b[i] * b[i] + } + + s := math32.Sqrt(aa) * math32.Sqrt(bb) + + d = (1 - ab/s) / 2 + + return +} + +// Type Class for Cosine distance +type Cosine int + +func (Cosine) Distance(a, b []float32) float32 { + return CosineF32(a, b) +} diff --git a/internal/pure/euclidean.go b/internal/pure/euclidean.go new file mode 100644 index 0000000..64b9c4e --- /dev/null +++ b/internal/pure/euclidean.go @@ -0,0 +1,32 @@ +// +// Copyright (C) 2024 Dmitry Kolesnikov +// +// This file may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// https://github.com/kshard/vector +// + +package pure + +const ENABLED_EUCLIDEAN = false + +// Squared Euclidean distance between two vectors +func EuclideanF32(a, b []float32) (d float32) { + if len(a) != len(b) { + panic("vectors must have equal lengths") + } + + for i := 0; i < len(a); i++ { + x := a[i] - b[i] + d += x * x + } + + return +} + +// Type Class for Euclidean distance +type Euclidean int + +func (Euclidean) Distance(a, b []float32) float32 { + return EuclideanF32(a, b) +} diff --git a/internal/simd/cosine_arm64.go b/internal/simd/cosine_arm64.go new file mode 100644 index 0000000..0b2fce2 --- /dev/null +++ b/internal/simd/cosine_arm64.go @@ -0,0 +1,13 @@ +//go:build arm64 + +// +// Copyright (C) 2024 Dmitry Kolesnikov +// +// This file may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// https://github.com/kshard/vector +// + +package simd + +var ENABLED_COSINE = false diff --git a/internal/simd/euclidean_arm64.go b/internal/simd/euclidean_arm64.go new file mode 100644 index 0000000..4ba4475 --- /dev/null +++ b/internal/simd/euclidean_arm64.go @@ -0,0 +1,30 @@ +//go:build arm64 + +// +// Copyright (C) 2024 Dmitry Kolesnikov +// +// This file may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// https://github.com/kshard/vector +// + +package simd + +import ( + "golang.org/x/sys/cpu" +) + +var ENABLED_EUCLIDEAN = cpu.ARM64.HasASIMD + +// Squared Euclidean distance between two vectors. +// +//go:noescape +func EuclideanF32(a, b, c []float32) + +// Type Class for Euclidean distance +type Euclidean [4]float32 + +func (d Euclidean) Distance(a, b []float32) float32 { + EuclideanF32(a, b, d[:]) + return d[3] + d[2] + d[1] + d[0] +} diff --git a/internal/simd/euclidean_arm64.s b/internal/simd/euclidean_arm64.s new file mode 100644 index 0000000..ee0761a --- /dev/null +++ b/internal/simd/euclidean_arm64.s @@ -0,0 +1,42 @@ +// +// Copyright (C) 2024 Dmitry Kolesnikov +// +// This file may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// https://github.com/kshard/vector +// + +#include "textflag.h" + +// func EuclideanF32(a, b, c []float32) float32 +TEXT ·EuclideanF32(SB), NOSPLIT, $0 + MOVD a_base + 0(FP), R0 + MOVD b_base + 24(FP), R1 + MOVD c_base + 48(FP), R2 + + MOVD a_size + (8)(FP), R20 + MOVD $4, R8 + MUL R20, R8 + ADD R0, R8, R8 + + // Drop one pointer since we compare after the loop + MOVD $16, R7 + SUB R7, R8, R8 + + WORD $0x4ea0d400; // fsub.4s v0, v0, v0 + +loop: + + WORD $0x4cdf7801; // ld1.4s { v1 }, [x0], #16 + WORD $0x4cdf7822; // ld1.4s { v2 }, [x1], #16 + WORD $0x4ea2d423; // fsub.4s v3, v1, v2 + WORD $0x6e23dc63; // fmul.4s v3, v3, v3 + WORD $0x4e23d400; // fadd.4s v0, v0, v3 + + CMP R0, R8 + BGE loop + + WORD $0x4c9f7840; // st1.4s { v0 }, [x2], #16 + + RET + diff --git a/internal/simd/noasm.go b/internal/simd/noasm.go new file mode 100644 index 0000000..ffa6f5a --- /dev/null +++ b/internal/simd/noasm.go @@ -0,0 +1,20 @@ +//go:build !arm64 + +// +// Copyright (C) 2024 Dmitry Kolesnikov +// +// This file may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// https://github.com/kshard/vector +// + +package simd + +var ( + ENABLED_EUCLIDEAN = false + ENABLED_COSINE = false +) + +func EuclideanF32(a, b, c []float32) { + panic("SIMD not available") +} diff --git a/types.go b/types.go new file mode 100644 index 0000000..6570720 --- /dev/null +++ b/types.go @@ -0,0 +1,97 @@ +// +// Copyright (C) 2024 Dmitry Kolesnikov +// +// This file may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// https://github.com/kshard/vector +// + +package vector + +import ( + "github.com/kshard/vector/internal/noasm" + "github.com/kshard/vector/internal/pure" + "github.com/kshard/vector/internal/simd" +) + +// Vector of float32 +type F32 = []float32 + +const ( + EUCLIDEAN_WITH_PURE = iota + EUCLIDEAN_WITH_NOASM + EUCLIDEAN_WITH_SIMD +) + +// Squared Euclidean distance between two vectors +func Euclidean() interface{ Distance(F32, F32) float32 } { + switch euclideanConfig() { + case EUCLIDEAN_WITH_PURE: + return pure.Euclidean(0) + case EUCLIDEAN_WITH_NOASM: + return noasm.Euclidean(0) + case EUCLIDEAN_WITH_SIMD: + return simd.Euclidean{} + } + + return nil +} + +func euclideanConfig() int { + if simd.ENABLED_EUCLIDEAN { + return EUCLIDEAN_WITH_SIMD + } + + if noasm.ENABLED_EUCLIDEAN { + return EUCLIDEAN_WITH_NOASM + } + + return EUCLIDEAN_WITH_PURE +} + +const ( + COSINE_WITH_PURE = iota + COSINE_WITH_NOASM + COSINE_WITH_SIMD +) + +// Cosine Distance +func Cosine() interface{ Distance(F32, F32) float32 } { + switch cosineConfig() { + case COSINE_WITH_PURE: + return pure.Cosine(0) + case COSINE_WITH_NOASM: + return noasm.Cosine(0) + // case COSINE_WITH_SIMD: + // return simd.Euclidean{} + } + + return nil +} + +func cosineConfig() int { + if simd.ENABLED_COSINE { + return COSINE_WITH_SIMD + } + + if noasm.ENABLED_COSINE { + return COSINE_WITH_NOASM + } + + return COSINE_WITH_PURE +} + +const ( + CONFIG_EUCLIDEAN = "euclidean" + CONFIG_COSINE = "cosine" +) + +// Info about config +func Info() map[string]int { + info := map[string]int{} + + info[CONFIG_EUCLIDEAN] = euclideanConfig() + info[CONFIG_COSINE] = cosineConfig() + + return info +} diff --git a/vector_test.go b/vector_test.go new file mode 100644 index 0000000..c406196 --- /dev/null +++ b/vector_test.go @@ -0,0 +1,141 @@ +// +// Copyright (C) 2024 Dmitry Kolesnikov +// +// This file may be modified and distributed under the terms +// of the MIT license. See the LICENSE file for details. +// https://github.com/kshard/vector +// + +package vector_test + +import ( + "math/rand" + "testing" + + "github.com/kshard/vector" + "github.com/kshard/vector/internal/noasm" + "github.com/kshard/vector/internal/pure" + "github.com/kshard/vector/internal/simd" +) + +var ( + n = 300 + d float32 + a vector.F32 + b vector.F32 +) + +func init() { + a = randF32() + b = randF32() + + for i := 0; i < n; i++ { + a[i] = rand.Float32() + b[i] = rand.Float32() + } +} + +func randF32() vector.F32 { + v := make(vector.F32, n) + for i := 0; i < n; i++ { + v[i] = rand.Float32() + } + return v +} + +func equal(a, b float32) bool { + d := a - b + return -1e-4 < d && d < 1e-4 +} + +func TestNoAsmEuclideanF32(t *testing.T) { + euc := pure.Euclidean(0) + sut := noasm.Euclidean(0) + + for i := 0; i < n*100; i++ { + a := randF32() + b := randF32() + + g := euc.Distance(a, b) + d := sut.Distance(a, b) + if !equal(d, g) { + t.Errorf("failed distance") + } + } +} + +func TestSIMDEuclideanF32(t *testing.T) { + if !simd.ENABLED_EUCLIDEAN { + return + } + + euc := pure.Euclidean(0) + sut := simd.Euclidean{} + + for i := 0; i < n*100; i++ { + a := randF32() + b := randF32() + + g := euc.Distance(a, b) + d := sut.Distance(a, b) + if !equal(d, g) { + t.Errorf("failed distance") + } + } +} + +func TestNoAsmCosineF32(t *testing.T) { + euc := pure.Cosine(0) + sut := noasm.Cosine(0) + + for i := 0; i < n*100; i++ { + a := randF32() + b := randF32() + + g := euc.Distance(a, b) + d := sut.Distance(a, b) + if !equal(d, g) { + t.Errorf("failed distance") + } + } +} + +func BenchmarkPureEuclideanF32(t *testing.B) { + euc := pure.Euclidean(0) + + for i := t.N; i > 0; i-- { + d = euc.Distance(a, b) + } +} + +func BenchmarkNoAsmEuclideanF32(t *testing.B) { + euc := noasm.Euclidean(0) + + for i := t.N; i > 0; i-- { + d = euc.Distance(a, b) + } +} + +func BenchmarkSIMDEuclideanF32(t *testing.B) { + euc := simd.Euclidean{} + + for i := t.N; i > 0; i-- { + d = euc.Distance(a, b) + } +} + +func BenchmarkPureCosineF32(t *testing.B) { + cos := pure.Cosine(0) + + for i := t.N; i > 0; i-- { + d = cos.Distance(a, b) + } +} + +func BenchmarkNoAsmCosineF32(t *testing.B) { + cos := noasm.Cosine(0) + + for i := t.N; i > 0; i-- { + d = cos.Distance(a, b) + } +} From 69a4bf5698965848039d0348b4c58ffe4cfab46e Mon Sep 17 00:00:00 2001 From: Dmitry Kolesnikov Date: Wed, 28 Feb 2024 00:13:22 +0200 Subject: [PATCH 2/3] fix tabs in asm code --- internal/simd/euclidean_arm64.s | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/simd/euclidean_arm64.s b/internal/simd/euclidean_arm64.s index ee0761a..1c3b2f1 100644 --- a/internal/simd/euclidean_arm64.s +++ b/internal/simd/euclidean_arm64.s @@ -17,7 +17,7 @@ TEXT ·EuclideanF32(SB), NOSPLIT, $0 MOVD a_size + (8)(FP), R20 MOVD $4, R8 MUL R20, R8 - ADD R0, R8, R8 + ADD R0, R8, R8 // Drop one pointer since we compare after the loop MOVD $16, R7 @@ -36,7 +36,7 @@ loop: CMP R0, R8 BGE loop - WORD $0x4c9f7840; // st1.4s { v0 }, [x2], #16 + WORD $0x4c9f7840; // st1.4s { v0 }, [x2], #16 - RET + RET From fe9163ad9132c5e3021353d970f5ba55c57efa46 Mon Sep 17 00:00:00 2001 From: Dmitry Kolesnikov Date: Wed, 28 Feb 2024 00:15:16 +0200 Subject: [PATCH 3/3] fix noasm for simd --- internal/simd/noasm.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/simd/noasm.go b/internal/simd/noasm.go index ffa6f5a..845236a 100644 --- a/internal/simd/noasm.go +++ b/internal/simd/noasm.go @@ -18,3 +18,10 @@ var ( func EuclideanF32(a, b, c []float32) { panic("SIMD not available") } + +// Type Class for Euclidean distance +type Euclidean [4]float32 + +func (d Euclidean) Distance(a, b []float32) float32 { + panic("SIMD not available") +}