簡體   English   中英

在C#中計算兩個字節數組之間歐氏距離的最快方法是什么?

[英]What is the fastest way of calculating the Euclidean distance between two byte arrays in C#

我需要盡可能快地找到歐幾里德距離內兩個字節數組之間最接近的匹配。

到目前為止我已經測試了這段代碼。

byte[] hash1 = new byte[200];
byte[] hash2 = new byte[200];

int distanceSquared = 0;
int diff;

for (int i = 0; i < 200; i++)
{
    diff = hash1[i] - hash2[i];
    distanceSquared += diff * diff;                
}

我能以某種方式加速這段代碼嗎?

您可以使用System.Numerics.Vectors進行向量化...這里最丑陋的一點是需要從byte到“int”加寬以避免舍入問題,但是...它的工作速度超過兩倍:

Basic: 2313122, 58ms
Vectorized: 2313122, 18ms

碼:

using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.InteropServices;

static class Program
{

    static void Main()
    {
        int len = 200;
        byte[] hash1 = new byte[len];
        byte[] hash2 = new byte[len];

        var rand = new Random(123456);
        rand.NextBytes(hash1);
        rand.NextBytes(hash2);

        Run(nameof(Basic), Basic, hash1, hash2);
        Run(nameof(Vectorized), Vectorized, hash1, hash2);
    }

    static void Run(string caption, Func<byte[], byte[], int> func, byte[] x, byte[] y, int repeat = 500000)
    {
        var timer = Stopwatch.StartNew();
        int result = 0;
        for (int i = 0; i < repeat; i++)
        {
            result = func(x, y);
        }
        timer.Stop();
        Console.WriteLine($"{caption}: {result}, {timer.ElapsedMilliseconds}ms");
    }

    static int Basic(byte[] hash1, byte[] hash2)
    {
        int distanceSquared = 0;
        for (int i = 0; i < hash1.Length; i++)
        {
            var diff = hash1[i] - hash2[i];
            distanceSquared += diff * diff;
        }
        return distanceSquared;
    }
    static int Vectorized(byte[] hash1, byte[] hash2)
    {
        int start, distanceSquared;
        if (Vector.IsHardwareAccelerated)
        {
            var sum = Vector<int>.Zero;
            var vec1 = MemoryMarshal.Cast<byte, Vector<byte>>(hash1);
            var vec2 = MemoryMarshal.Cast<byte, Vector<byte>>(hash2);

            for (int i = 0; i < vec1.Length; i++)
            {
                // widen and hard cast needed here to avoid overflow problems
                Vector.Widen(vec1[i], out var l1, out var r1);
                Vector.Widen(vec2[i], out var l2, out var r2);
                Vector<short> lt1 = Vector.AsVectorInt16(l1), rt1 = Vector.AsVectorInt16(r1);
                Vector<short> lt2 = Vector.AsVectorInt16(l2), rt2 = Vector.AsVectorInt16(r2);
                Vector.Widen(lt1 - lt2, out var dl1, out var dl2);
                Vector.Widen(rt1 - rt2, out var dr1, out var dr2);
                sum += (dl1 * dl1) + (dl2 * dl2) + (dr1 * dr1) + (dr2 * dr2);
            }
            start = vec1.Length * Vector<byte>.Count;
            distanceSquared = 0;
            for (int i = 0; i < Vector<int>.Count; i++)
                distanceSquared += sum[i];
        }
        else
        {
            start = distanceSquared = 0;
        }
        for (int i = start; i < hash1.Length; i++)
        {
            var diff = hash1[i] - hash2[i];
            distanceSquared += diff * diff;
        }
        return distanceSquared;
    }
}

如果您使用.NET Core 3(現在它是預覽但它接近RC),您可以使用硬件內在函數來加速計算。 例如,Microsoft使用它來加速機器學習操作

你做這個操作diff = hash1[i] - hash2[i]; 使用: VPSUBB硬件指令。 然后改變distanceSquared += diff * diff; PMADDUBSW硬件指令。

這應該是最快的方法,也許您應該調查其他硬件指令。 我希望這可以幫到你。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM