Exemplo n.º 1
0
        public static unsafe void CalculateDiagonalSection_Avx2 <T>(void *refDiag1Ptr, void *refDiag2Ptr, char *sourcePtr, char *targetPtr, ref int rowIndex, int columnIndex) where T : struct
        {
            if (typeof(T) == typeof(int))
            {
                var diag1Ptr = (int *)refDiag1Ptr;
                var diag2Ptr = (int *)refDiag2Ptr;

                var sourceVector = Avx2.ConvertToVector256Int32((ushort *)sourcePtr + rowIndex - Vector256 <T> .Count);
                var targetVector = Avx2.ConvertToVector256Int32((ushort *)targetPtr + columnIndex - 1);
                targetVector = Avx2.Shuffle(targetVector, 0x1b);
                targetVector = Avx2.Permute2x128(targetVector, targetVector, 1);
                var substitutionCostAdjustment = Avx2.CompareEqual(sourceVector, targetVector);

                var substitutionCost = Avx2.Add(
                    Avx.LoadDquVector256(diag1Ptr + rowIndex - Vector256 <T> .Count),
                    substitutionCostAdjustment
                    );
                var deleteCost = Avx.LoadDquVector256(diag2Ptr + rowIndex - (Vector256 <T> .Count - 1));
                var insertCost = Avx.LoadDquVector256(diag2Ptr + rowIndex - Vector256 <T> .Count);

                var localCost = Avx2.Min(Avx2.Min(insertCost, deleteCost), substitutionCost);
                localCost = Avx2.Add(localCost, Vector256.Create(1));

                Avx.Store(diag1Ptr + rowIndex - (Vector256 <T> .Count - 1), localCost);
            }
            else if (typeof(T) == typeof(ushort))
            {
                var diag1Ptr = (ushort *)refDiag1Ptr;
                var diag2Ptr = (ushort *)refDiag2Ptr;

                var sourceVector = Avx.LoadDquVector256((ushort *)sourcePtr + rowIndex - Vector256 <T> .Count);
                var targetVector = Avx.LoadDquVector256((ushort *)targetPtr + columnIndex - 1);
                targetVector = Avx2.Shuffle(targetVector.AsByte(), REVERSE_USHORT_AS_BYTE_256).AsUInt16();
                targetVector = Avx2.Permute2x128(targetVector, targetVector, 1);
                var substitutionCostAdjustment = Avx2.CompareEqual(sourceVector, targetVector);

                var substitutionCost = Avx2.Add(
                    Avx.LoadDquVector256(diag1Ptr + rowIndex - Vector256 <T> .Count),
                    substitutionCostAdjustment
                    );
                var deleteCost = Avx.LoadDquVector256(diag2Ptr + rowIndex - (Vector256 <T> .Count - 1));
                var insertCost = Avx.LoadDquVector256(diag2Ptr + rowIndex - Vector256 <T> .Count);

                var localCost = Avx2.Min(Avx2.Min(insertCost, deleteCost), substitutionCost);
                localCost = Avx2.Add(localCost, Vector256.Create((ushort)1));

                Avx.Store(diag1Ptr + rowIndex - (Vector256 <T> .Count - 1), localCost);
            }
        }
        static unsafe void Main(string[] args)
        {
            var bytes = stackalloc sbyte[]
            {
                0x48, 0x64, 0x6A, 0x69, 0x6B, 0x1B, 0x4D, 0x5C,
                0x59, 0x63, 0x57, 0x67, 0x14, 0x4A, 0x61, 0x63,
                0x5C, 0x53, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A,
                0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02,
            };

            var scalarDiff = stackalloc sbyte[]
            {
                0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
                0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
            };

            var avx2Diff = stackalloc sbyte[]
            {
                0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x09, 0x09,
                0x0A, 0x11, 0x18, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
                0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
            };

            if (Avx2.IsSupported)
            {
                var data = Avx.LoadDquVector256(bytes);
                var diff = Avx.LoadDquVector256(avx2Diff);
                Avx.Store(bytes, Avx2.Add(data, diff));
            }
            else
            {
                for (var i = 0; i < 32; i++)
                {
                    bytes[i] += scalarDiff[i];
                }
            }

            var s = new string(bytes, 0, 32, Encoding.UTF8);

            Console.WriteLine(s);
        }
Exemplo n.º 3
0
        internal static unsafe Vector256 <T> LoadDquVector256(T *address)
        {
            if (typeof(T) == typeof(sbyte))
            {
                return(Avx.LoadDquVector256((sbyte *)address).As <sbyte, T>());
            }
            if (typeof(T) == typeof(byte))
            {
                return(Avx.LoadDquVector256((byte *)address).As <byte, T>());
            }
            if (typeof(T) == typeof(short))
            {
                return(Avx.LoadDquVector256((short *)address).As <short, T>());
            }
            if (typeof(T) == typeof(ushort))
            {
                return(Avx.LoadDquVector256((ushort *)address).As <ushort, T>());
            }
            if (typeof(T) == typeof(int))
            {
                return(Avx.LoadDquVector256((int *)address).As <int, T>());
            }
            if (typeof(T) == typeof(uint))
            {
                return(Avx.LoadDquVector256((uint *)address).As <uint, T>());
            }
            if (typeof(T) == typeof(long))
            {
                return(Avx.LoadDquVector256((long *)address).As <long, T>());
            }
            if (typeof(T) == typeof(ulong))
            {
                return(Avx.LoadDquVector256((ulong *)address).As <ulong, T>());
            }

            throw new NotSupportedException();
        }
Exemplo n.º 4
0
        static unsafe int Main(string[] args)
        {
            int testResult = Pass;

            if (Avx.IsSupported)
            {
                using (TestTable <int> intTable = new TestTable <int>(new int[8] {
                    1, -5, 100, 0, 1, 2, 3, 4
                }, new int[8]))
                {
                    var vf = Avx.LoadDquVector256((int *)(intTable.inArrayPtr));
                    Unsafe.Write(intTable.outArrayPtr, vf);

                    if (!intTable.CheckResult((x, y) => x == y))
                    {
                        Console.WriteLine("AVX LoadDquVector256 failed on int:");
                        foreach (var item in intTable.outArray)
                        {
                            Console.Write(item + ", ");
                        }
                        Console.WriteLine();
                        testResult = Fail;
                    }
                }

                using (TestTable <uint> intTable = new TestTable <uint>(new uint[8] {
                    1, 5, 100, 0, 1, 2, 3, 4
                }, new uint[8]))
                {
                    var vf = Avx.LoadDquVector256((uint *)(intTable.inArrayPtr));
                    Unsafe.Write(intTable.outArrayPtr, vf);

                    if (!intTable.CheckResult((x, y) => x == y))
                    {
                        Console.WriteLine("AVX LoadDquVector256 failed on uint:");
                        foreach (var item in intTable.outArray)
                        {
                            Console.Write(item + ", ");
                        }
                        Console.WriteLine();
                        testResult = Fail;
                    }
                }

                using (TestTable <long> intTable = new TestTable <long>(new long[4] {
                    1, -5, 100, 0
                }, new long[4]))
                {
                    var vf = Avx.LoadDquVector256((long *)(intTable.inArrayPtr));
                    Unsafe.Write(intTable.outArrayPtr, vf);

                    if (!intTable.CheckResult((x, y) => x == y))
                    {
                        Console.WriteLine("AVX LoadDquVector256 failed on long:");
                        foreach (var item in intTable.outArray)
                        {
                            Console.Write(item + ", ");
                        }
                        Console.WriteLine();
                        testResult = Fail;
                    }
                }

                using (TestTable <ulong> intTable = new TestTable <ulong>(new ulong[4] {
                    1, 5, 100, 0
                }, new ulong[4]))
                {
                    var vf = Avx.LoadDquVector256((ulong *)(intTable.inArrayPtr));
                    Unsafe.Write(intTable.outArrayPtr, vf);

                    if (!intTable.CheckResult((x, y) => x == y))
                    {
                        Console.WriteLine("AVX LoadDquVector256 failed on ulong:");
                        foreach (var item in intTable.outArray)
                        {
                            Console.Write(item + ", ");
                        }
                        Console.WriteLine();
                        testResult = Fail;
                    }
                }

                using (TestTable <short> intTable = new TestTable <short>(new short[16] {
                    1, -5, 100, 0, 1, 2, 3, 4, 1, -5, 100, 0, 1, 2, 3, 4
                }, new short[16]))
                {
                    var vf = Avx.LoadDquVector256((short *)(intTable.inArrayPtr));
                    Unsafe.Write(intTable.outArrayPtr, vf);

                    if (!intTable.CheckResult((x, y) => x == y))
                    {
                        Console.WriteLine("AVX LoadDquVector256 failed on short:");
                        foreach (var item in intTable.outArray)
                        {
                            Console.Write(item + ", ");
                        }
                        Console.WriteLine();
                        testResult = Fail;
                    }
                }

                using (TestTable <ushort> intTable = new TestTable <ushort>(new ushort[16] {
                    1, 5, 100, 0, 1, 2, 3, 4, 1, 5, 100, 0, 1, 2, 3, 4
                }, new ushort[16]))
                {
                    var vf = Avx.LoadDquVector256((ushort *)(intTable.inArrayPtr));
                    Unsafe.Write(intTable.outArrayPtr, vf);

                    if (!intTable.CheckResult((x, y) => x == y))
                    {
                        Console.WriteLine("AVX LoadDquVector256 failed on ushort:");
                        foreach (var item in intTable.outArray)
                        {
                            Console.Write(item + ", ");
                        }
                        Console.WriteLine();
                        testResult = Fail;
                    }
                }

                using (TestTable <byte> intTable = new TestTable <byte>(new byte[32] {
                    1, 5, 100, 0, 1, 2, 3, 4, 1, 5, 100, 0, 1, 2, 3, 4, 1, 5, 100, 0, 1, 2, 3, 4, 1, 5, 100, 0, 1, 2, 3, 4
                }, new byte[32]))
                {
                    var vf = Avx.LoadDquVector256((byte *)(intTable.inArrayPtr));
                    Unsafe.Write(intTable.outArrayPtr, vf);

                    if (!intTable.CheckResult((x, y) => x == y))
                    {
                        Console.WriteLine("AVX LoadDquVector256 failed on byte:");
                        foreach (var item in intTable.outArray)
                        {
                            Console.Write(item + ", ");
                        }
                        Console.WriteLine();
                        testResult = Fail;
                    }
                }

                using (TestTable <sbyte> intTable = new TestTable <sbyte>(new sbyte[32] {
                    1, -5, 100, 0, 1, 2, 3, 4, 1, -5, 100, 0, 1, 2, 3, 4, 1, -5, 100, 0, 1, 2, 3, 4, 1, -5, 100, 0, 1, 2, 3, 4
                }, new sbyte[32]))
                {
                    var vf = Avx.LoadDquVector256((sbyte *)(intTable.inArrayPtr));
                    Unsafe.Write(intTable.outArrayPtr, vf);

                    if (!intTable.CheckResult((x, y) => x == y))
                    {
                        Console.WriteLine("AVX LoadDquVector256 failed on sbyte:");
                        foreach (var item in intTable.outArray)
                        {
                            Console.Write(item + ", ");
                        }
                        Console.WriteLine();
                        testResult = Fail;
                    }
                }
            }

            return(testResult);
        }
Exemplo n.º 5
0
        public static unsafe void TrimLengthOfMatchingCharacters(char *sourcePtr, char *targetPtr, ref int sourceLength, ref int targetLength)
        {
            var searchLength = Math.Min(sourceLength, targetLength);

#if NETCOREAPP
            var sourceUShortPtr = (ushort *)sourcePtr;
            var targetUShortPtr = (ushort *)targetPtr;

            if (Sse2.IsSupported && searchLength >= Vector128 <ushort> .Count * 2)
            {
                if (Avx2.IsSupported)
                {
                    while (searchLength >= Vector256 <ushort> .Count)
                    {
                        var sourceVector = Avx.LoadDquVector256(sourceUShortPtr + sourceLength - Vector256 <ushort> .Count);
                        var targetVector = Avx.LoadDquVector256(targetUShortPtr + targetLength - Vector256 <ushort> .Count);
                        var match        = (uint)Avx2.MoveMask(
                            Avx2.CompareEqual(
                                sourceVector,
                                targetVector
                                ).AsByte()
                            );

                        if (match == uint.MaxValue)
                        {
                            sourceLength -= Vector256 <ushort> .Count;
                            targetLength -= Vector256 <ushort> .Count;
                            searchLength -= Vector256 <ushort> .Count;
                            continue;
                        }

                        var lastMatch = BitOperations.LeadingZeroCount(match ^ uint.MaxValue) / sizeof(ushort);
                        sourceLength -= lastMatch;
                        targetLength -= lastMatch;
                        return;
                    }
                }

                while (searchLength >= Vector128 <ushort> .Count)
                {
                    var sourceVector = Sse2.LoadVector128(sourceUShortPtr + sourceLength - Vector128 <ushort> .Count);
                    var targetVector = Sse2.LoadVector128(targetUShortPtr + targetLength - Vector128 <ushort> .Count);
                    var match        = (uint)Sse2.MoveMask(
                        Sse2.CompareEqual(
                            sourceVector,
                            targetVector
                            ).AsByte()
                        );

                    if (match == ushort.MaxValue)
                    {
                        sourceLength -= Vector128 <ushort> .Count;
                        targetLength -= Vector128 <ushort> .Count;
                        searchLength -= Vector128 <ushort> .Count;
                        continue;
                    }

                    var lastMatch = BitOperations.LeadingZeroCount(match ^ ushort.MaxValue) / sizeof(ushort) - Vector128 <ushort> .Count;
                    sourceLength -= lastMatch;
                    targetLength -= lastMatch;
                    return;
                }
            }
#endif
            sourcePtr += sourceLength - 1;
            targetPtr += targetLength - 1;

            while (searchLength > 0 && sourcePtr[0] == targetPtr[0])
            {
                sourcePtr--;
                targetPtr--;
                sourceLength--;
                targetLength--;
                searchLength--;
            }
        }
Exemplo n.º 6
0
        public static unsafe int GetIndexOfFirstNonMatchingCharacter(char *sourcePtr, char *targetPtr, int sourceLength, int targetLength)
        {
            var searchLength = Math.Min(sourceLength, targetLength);
            var index        = 0;

#if NETCOREAPP
            var sourceUShortPtr = (ushort *)sourcePtr;
            var targetUShortPtr = (ushort *)targetPtr;

            if (Sse2.IsSupported && searchLength >= Vector128 <ushort> .Count * 2)
            {
                if (Avx2.IsSupported)
                {
                    while (searchLength >= Vector256 <ushort> .Count)
                    {
                        var sourceVector = Avx.LoadDquVector256(sourceUShortPtr + index);
                        var targetVector = Avx.LoadDquVector256(targetUShortPtr + index);
                        var match        = (uint)Avx2.MoveMask(
                            Avx2.CompareEqual(
                                sourceVector,
                                targetVector
                                ).AsByte()
                            );

                        if (match == uint.MaxValue)
                        {
                            index        += Vector256 <ushort> .Count;
                            searchLength -= Vector256 <ushort> .Count;
                            continue;
                        }

                        index += BitOperations.TrailingZeroCount(match ^ uint.MaxValue) / sizeof(ushort);
                        return(index);
                    }
                }

                while (searchLength >= Vector128 <ushort> .Count)
                {
                    var sourceVector = Sse2.LoadVector128(sourceUShortPtr + index);
                    var targetVector = Sse2.LoadVector128(targetUShortPtr + index);
                    var match        = (uint)Sse2.MoveMask(
                        Sse2.CompareEqual(
                            sourceVector,
                            targetVector
                            ).AsByte()
                        );

                    if (match == ushort.MaxValue)
                    {
                        index        += Vector128 <ushort> .Count;
                        searchLength -= Vector128 <ushort> .Count;
                        continue;
                    }

                    index += BitOperations.TrailingZeroCount(match ^ ushort.MaxValue) / sizeof(ushort);
                    return(index);
                }
            }
#endif

            while (searchLength > 0 && sourcePtr[index] == targetPtr[index])
            {
                searchLength--;
                index++;
            }

            return(index);
        }
        public unsafe static int GetDistance(string source, string target)
        {
            var startIndex = 0;
            var sourceEnd  = source.Length;
            var targetEnd  = target.Length;

            fixed(char *sourcePtr = source)
            fixed(char *targetPtr = target)
            {
                var charactersAvailableToTrim = Math.Min(targetEnd, sourceEnd);

                if (Avx2.IsSupported)
                {
                    var sourceUShortPtr = (ushort *)sourcePtr;
                    var targetUShortPtr = (ushort *)targetPtr;

                    while (charactersAvailableToTrim >= Vector256 <ushort> .Count)
                    {
                        var match = (uint)Avx2.MoveMask(
                            Avx2.CompareEqual(
                                Avx.LoadDquVector256(sourceUShortPtr + startIndex),
                                Avx.LoadDquVector256(targetUShortPtr + startIndex)
                                ).AsByte()
                            );

                        if (match != uint.MaxValue)
                        {
                            var remaining = BitOperations.TrailingZeroCount(match ^ uint.MaxValue) / sizeof(ushort);
                            startIndex += remaining;
                            charactersAvailableToTrim -= remaining;
                            break;
                        }

                        startIndex += Vector256 <ushort> .Count;
                        charactersAvailableToTrim -= Vector256 <ushort> .Count;
                    }

                    while (charactersAvailableToTrim >= Vector256 <ushort> .Count)
                    {
                        var match = (uint)Avx2.MoveMask(
                            Avx2.CompareEqual(
                                Avx.LoadDquVector256(sourceUShortPtr + sourceEnd - Vector256 <ushort> .Count),
                                Avx.LoadDquVector256(targetUShortPtr + targetEnd - Vector256 <ushort> .Count)
                                ).AsByte()
                            );

                        if (match != uint.MaxValue)
                        {
                            var lastMatch = BitOperations.LeadingZeroCount(match ^ uint.MaxValue) / sizeof(ushort);
                            sourceEnd -= lastMatch;
                            targetEnd -= lastMatch;
                            break;
                        }

                        sourceEnd -= Vector256 <ushort> .Count;
                        targetEnd -= Vector256 <ushort> .Count;
                        charactersAvailableToTrim -= Vector256 <ushort> .Count;
                    }
                }

                while (charactersAvailableToTrim > 0 && source[startIndex] == target[startIndex])
                {
                    charactersAvailableToTrim--;
                    startIndex++;
                }

                while (charactersAvailableToTrim > 0 && source[sourceEnd - 1] == target[targetEnd - 1])
                {
                    charactersAvailableToTrim--;
                    sourceEnd--;
                    targetEnd--;
                }
            }

            var sourceLength = sourceEnd - startIndex;
            var targetLength = targetEnd - startIndex;

            if (sourceLength == 0)
            {
                return(targetLength);
            }

            if (targetLength == 0)
            {
                return(sourceLength);
            }

            var sourceSpan = source.AsSpan().Slice(startIndex, sourceLength);
            var targetSpan = target.AsSpan().Slice(startIndex, targetLength);

            var previousRow = ArrayPool <int> .Shared.Rent(targetSpan.Length);

            var allOnesVector = Vector128.Create(1);

            fixed(int *previousRowPtr = previousRow)
            fixed(char *sourcePtr = sourceSpan)
            fixed(char *targetPtr = target)
            {
                for (var columnIndex = 0; columnIndex < targetLength; columnIndex++)
                {
                    previousRowPtr[columnIndex] = columnIndex;
                }

                for (var rowIndex = 0; rowIndex < sourceLength; rowIndex++)
                {
                    var lastSubstitutionCost = rowIndex;
                    var lastInsertionCost    = rowIndex + 1;

                    var sourceChar = sourcePtr[rowIndex];

                    if (Sse41.IsSupported)
                    {
                        var lastSubstitutionCostVector = Vector128.Create(lastSubstitutionCost);
                        var lastInsertionCostVector    = Vector128.Create(lastInsertionCost);

                        for (var columnIndex = 0; columnIndex < targetLength; columnIndex++)
                        {
                            var localCostVector        = lastSubstitutionCostVector;
                            var lastDeletionCostVector = Vector128.Create(previousRowPtr[columnIndex]);
                            if (sourceChar != targetPtr[columnIndex])
                            {
                                localCostVector = Sse2.Add(
                                    Sse41.Min(
                                        Sse41.Min(
                                            lastInsertionCostVector,
                                            localCostVector
                                            ),
                                        lastDeletionCostVector
                                        ),
                                    allOnesVector
                                    );
                            }
                            lastInsertionCostVector     = localCostVector;
                            previousRowPtr[columnIndex] = localCostVector.GetElement(0);
                            lastSubstitutionCostVector  = lastDeletionCostVector;
                        }
                    }
                    else
                    {
                        for (var columnIndex = 0; columnIndex < targetLength; columnIndex++)
                        {
                            var localCost    = lastSubstitutionCost;
                            var deletionCost = previousRowPtr[columnIndex];
                            if (sourceChar != targetPtr[columnIndex])
                            {
                                localCost = Math.Min(lastInsertionCost, localCost);
                                localCost = Math.Min(deletionCost, localCost);
                                localCost++;
                            }
                            lastInsertionCost           = localCost;
                            previousRowPtr[columnIndex] = localCost;
                            lastSubstitutionCost        = deletionCost;
                        }
                    }
                }
            }

            var result = previousRow[targetSpan.Length - 1];

            ArrayPool <int> .Shared.Return(previousRow);

            return(result);
        }
        public unsafe static int GetDistance(string source, string target)
        {
            var startIndex = 0;
            var sourceEnd  = source.Length;
            var targetEnd  = target.Length;

            fixed(char *sourcePtr = source)
            fixed(char *targetPtr = target)
            {
                var charactersAvailableToTrim = Math.Min(targetEnd, sourceEnd);

                if (Avx2.IsSupported)
                {
                    var sourceUShortPtr = (ushort *)sourcePtr;
                    var targetUShortPtr = (ushort *)targetPtr;

                    while (charactersAvailableToTrim >= Vector256 <ushort> .Count)
                    {
                        var match = (uint)Avx2.MoveMask(
                            Avx2.CompareEqual(
                                Avx.LoadDquVector256(sourceUShortPtr + startIndex),
                                Avx.LoadDquVector256(targetUShortPtr + startIndex)
                                ).AsByte()
                            );

                        if (match != uint.MaxValue)
                        {
                            var remaining = BitOperations.TrailingZeroCount(match ^ uint.MaxValue) / sizeof(ushort);
                            startIndex += remaining;
                            charactersAvailableToTrim -= remaining;
                            break;
                        }

                        startIndex += Vector256 <ushort> .Count;
                        charactersAvailableToTrim -= Vector256 <ushort> .Count;
                    }

                    while (charactersAvailableToTrim >= Vector256 <ushort> .Count)
                    {
                        var match = (uint)Avx2.MoveMask(
                            Avx2.CompareEqual(
                                Avx.LoadDquVector256(sourceUShortPtr + sourceEnd - Vector256 <ushort> .Count),
                                Avx.LoadDquVector256(targetUShortPtr + targetEnd - Vector256 <ushort> .Count)
                                ).AsByte()
                            );

                        if (match != uint.MaxValue)
                        {
                            var lastMatch = BitOperations.LeadingZeroCount(match ^ uint.MaxValue) / sizeof(ushort);
                            sourceEnd -= lastMatch;
                            targetEnd -= lastMatch;
                            break;
                        }

                        sourceEnd -= Vector256 <ushort> .Count;
                        targetEnd -= Vector256 <ushort> .Count;
                        charactersAvailableToTrim -= Vector256 <ushort> .Count;
                    }
                }

                while (charactersAvailableToTrim > 0 && source[startIndex] == target[startIndex])
                {
                    charactersAvailableToTrim--;
                    startIndex++;
                }

                while (charactersAvailableToTrim > 0 && source[sourceEnd - 1] == target[targetEnd - 1])
                {
                    charactersAvailableToTrim--;
                    sourceEnd--;
                    targetEnd--;
                }
            }

            var sourceLength = sourceEnd - startIndex;
            var targetLength = targetEnd - startIndex;

            if (sourceLength == 0)
            {
                return(targetLength);
            }

            if (targetLength == 0)
            {
                return(sourceLength);
            }

            var sourceSpan = source.AsSpan().Slice(startIndex, sourceLength);
            var targetSpan = target.AsSpan().Slice(startIndex, targetLength);

            var previousRow = ArrayPool <int> .Shared.Rent(targetSpan.Length);

            var allOnesVector = Vector128.Create(1);

            fixed(int *previousRowPtr = previousRow)
            fixed(char *sourcePtr = sourceSpan)
            fixed(char *targetPtr = target)
            {
                var maximumNumberOfWorkers = Environment.ProcessorCount;
                var numberOfWorkers        = targetLength / MINIMUM_CHARACTERS_PER_THREAD;

                if (numberOfWorkers == 0)
                {
                    numberOfWorkers = 1;
                }
                else if (numberOfWorkers > maximumNumberOfWorkers)
                {
                    numberOfWorkers = maximumNumberOfWorkers;
                }

                var numberOfColumnsPerWorker = targetLength / numberOfWorkers;
                var remainderColumns         = targetLength % numberOfWorkers;

                var rowCountPtr          = stackalloc int[Environment.ProcessorCount];
                var columnBoundariesPool = ArrayPool <int[]> .Shared.Rent(numberOfWorkers + 1);

                //Initialise shared task boundaries
                for (var i = 0; i < numberOfWorkers + 1; i++)
                {
                    columnBoundariesPool[i] = ArrayPool <int> .Shared.Rent(sourceLength + 1);

                    columnBoundariesPool[i][0] = i * numberOfColumnsPerWorker;
                }
                columnBoundariesPool[numberOfWorkers][0] += remainderColumns;

                //Fill first column boundary (ColumnIndex = 0) with incrementing numbers
                fixed(int *startBoundaryPtr = columnBoundariesPool[0])
                {
                    for (var rowIndex = 0; rowIndex <= sourceLength; rowIndex++)
                    {
                        startBoundaryPtr[rowIndex] = rowIndex;
                    }
                }

                for (var workerIndex = 0; workerIndex < numberOfWorkers - 1; workerIndex++)
                {
                    var columnIndex = workerIndex * numberOfColumnsPerWorker;

                    ThreadPool.QueueUserWorkItem(WorkerTask, new WorkerState
                    {
                        RowCountPtr           = rowCountPtr,
                        WorkerIndex           = workerIndex,
                        ColumnIndex           = columnIndex,
                        SourcePtr             = sourcePtr,
                        SourceLength          = sourceLength,
                        TargetRegionPtr       = targetPtr + columnIndex,
                        TargetRegionLength    = numberOfColumnsPerWorker,
                        BackColumnBoundary    = columnBoundariesPool[workerIndex],
                        ForwardColumnBoundary = columnBoundariesPool[workerIndex + 1]
                    });
                }

                //Run last segment synchronously (ie. in the current thread)
                var lastWorkerIndex       = numberOfWorkers - 1;
                var lastWorkerColumnIndex = lastWorkerIndex * numberOfColumnsPerWorker;

                WorkerTask_CalculateRegion(new WorkerState
                {
                    RowCountPtr           = rowCountPtr,
                    WorkerIndex           = numberOfWorkers - 1,
                    ColumnIndex           = (numberOfWorkers - 1) * numberOfColumnsPerWorker,
                    SourcePtr             = sourcePtr,
                    SourceLength          = sourceLength,
                    TargetRegionPtr       = targetPtr + lastWorkerColumnIndex,
                    TargetRegionLength    = numberOfColumnsPerWorker + remainderColumns,
                    BackColumnBoundary    = columnBoundariesPool[lastWorkerIndex],
                    ForwardColumnBoundary = columnBoundariesPool[lastWorkerIndex + 1]
                });

                //Extract last value in forward column boundary of last task (the actual distance)
                var result = columnBoundariesPool[numberOfWorkers][sourceLength];

                //Cleanup
                //Return all column boundaries then the container of boundaries
                for (var i = 0; i < numberOfWorkers + 1; i++)
                {
                    ArrayPool <int> .Shared.Return(columnBoundariesPool[i]);
                }
                ArrayPool <int[]> .Shared.Return(columnBoundariesPool);

                return(result);
            }
        }