private static unsafe int CalculateDistance(string sourceString, int sourceLength, string targetString, int targetLength, int startIndex) { var arrayPool = ArrayPool <ushort> .Shared; var pooledArray = arrayPool.Rent(targetLength); Span <ushort> previousRow = pooledArray; ReadOnlySpan <char> source = sourceString.AsSpan().Slice(startIndex, sourceLength); ReadOnlySpan <char> target = targetString.AsSpan().Slice(startIndex, targetLength); //ArrayPool values are sometimes bigger than allocated, let's trim our span to exactly what we use previousRow = previousRow.Slice(0, targetLength); fixed(char *targetPtr = target) fixed(char *srcPtr = source) fixed(ushort *previousRowPtr = previousRow) { FillRow(previousRowPtr, targetLength); var rowIndex = 0; for (; rowIndex < sourceLength - 7; rowIndex += 8) { // todo max var temp = Vector128.Create(rowIndex); var diag = Sse42.PackUnsignedSaturate(temp, temp); var one = Vector128.Create((ushort)1); var left = Sse42.AddSaturate(diag, one); var sourceV = Sse42.LoadVector128((ushort *)(srcPtr + rowIndex)); var targetV = Vector128 <ushort> .Zero; var shift = Vector128.CreateScalar(ushort.MaxValue); // First 3 iterations fills the vector for (int columnIndex = 0; columnIndex < 7; columnIndex++) { // Shift in the next character targetV = Sse42.ShiftLeftLogical128BitLane(targetV, 2); targetV = Sse42.Insert(targetV, (ushort)targetPtr[columnIndex], 0); // Insert "(rowIndex + columnIndex + 1)" from the left var leftValue = Vector128.Create(rowIndex + columnIndex + 1); left = Sse42.Or(Sse42.And(shift, Sse42.PackUnsignedSaturate(leftValue, leftValue)), left); shift = Sse42.ShiftLeftLogical128BitLane(shift, 2); // compare source to target // alternativ, compare equal and OR with One var match = Sse42.CompareEqual(sourceV, targetV); var add = Sse42.AndNot(match, one); var next = Sse42.AddSaturate(diag, add); // Create next diag which is current up var up = Sse42.ShiftLeftLogical128BitLane(left, 2); up = Sse42.Insert(up, (ushort)previousRowPtr[columnIndex], 0); var tmp = Sse42.AddSaturate(Sse42.Min(left, up), one); next = Sse42.Min(next, tmp); left = next; diag = up; } previousRowPtr[0] = Sse42.Extract(left, 7); var writePtr = previousRowPtr + 1; for (int columnIndex = 8; columnIndex < targetLength; columnIndex++) { // Shift in the next character targetV = Sse42.ShiftLeftLogical128BitLane(targetV, 2); targetV = Sse42.Insert(targetV, (ushort)targetPtr[columnIndex], 0); // compare source to target // alternativ, compare equal and OR with One var match = Sse42.CompareEqual(sourceV, targetV); var add = Sse42.AndNot(match, one); var next = Sse42.AddSaturate(diag, add); // Create next diag which is current up var up = Sse42.ShiftLeftLogical128BitLane(left, 2); up = Sse42.Insert(up, (ushort)previousRowPtr[columnIndex], 0); var tmp = Sse42.AddSaturate(Sse42.Min(left, up), one); next = Sse42.Min(next, tmp); left = next; diag = up; // Store one value *writePtr = Sse42.Extract(next, 7); writePtr = writePtr + 1; // Store one value //previousRowPtr[columnIndex - 7] = Sse42.Extract(next, 7); } // Finish with last 3 items, dont read any more chars just extract them for (int i = targetLength - 7; i < previousRow.Length; i++) { // Shift in the next character targetV = Sse42.ShiftLeftLogical128BitLane(targetV, 2); // compare source to target // alternativ, compare equal and OR with One var match = Sse42.CompareEqual(sourceV, targetV); var add = Sse42.AndNot(match, one); var next = Sse42.AddSaturate(diag, add); // Create next diag which is current up var up = Sse42.ShiftLeftLogical128BitLane(left, 2); var tmp = Sse42.AddSaturate(Sse42.Min(left, up), one); next = Sse42.Min(next, tmp); left = next; diag = up; // Store one value previousRowPtr[i] = Sse42.Extract(next, 7); } #if DEBUG if (true) { Console.Write("prev values for row {0}:", rowIndex); for (int i = 0; i < targetLength; ++i) { Console.Write("{0} ", previousRow[i]); } Console.WriteLine(); } #endif } //Calculate Single Rows for (; rowIndex < sourceLength; rowIndex++) { var lastSubstitutionCost = rowIndex; var lastInsertionCost = rowIndex + 1; var sourcePrevChar = source[rowIndex]; #if DEBUG Console.Write("prev values for row {0}:", rowIndex); for (int i = 0; i < targetLength; ++i) { Console.Write("{0} ", previousRow[i]); } Console.WriteLine(); #endif CalculateRow(previousRowPtr, targetPtr, targetLength, sourcePrevChar, lastInsertionCost, lastSubstitutionCost); } } var result = previousRow[targetLength - 1]; arrayPool.Return(pooledArray); return(result); }
private static unsafe int CalculateDistance(string sourceString, int sourceLength, string targetString, int targetLength, int startIndex) { var arrayPool = ArrayPool <int> .Shared; var pooledArray = arrayPool.Rent(targetLength); Span <int> previousRow = pooledArray; ReadOnlySpan <char> source = sourceString.AsSpan().Slice(startIndex, sourceLength); ReadOnlySpan <char> target = targetString.AsSpan().Slice(startIndex, targetLength); //ArrayPool values are sometimes bigger than allocated, let's trim our span to exactly what we use previousRow = previousRow.Slice(0, targetLength); fixed(char *targetPtr = target) fixed(char *srcPtr = source) fixed(int *previousRowPtr = previousRow) { FillRow(previousRowPtr, targetLength); var rowIndex = 0; for (; rowIndex < sourceLength - 3; rowIndex += 4) { var diag = Vector128.Create(rowIndex); var left = Vector128.Create(rowIndex + 1); var sourceV = Sse42.ConvertToVector128Int32((short *)(srcPtr + rowIndex)); var targetV = Vector128 <int> .Zero; var one = Vector128.Create(1); // First 3 iterations fills the vector var shift = Vector128.CreateScalar(-1); for (int columnIndex = 0; columnIndex < 4; columnIndex++) { // Shift in the next character targetV = Sse42.ShiftLeftLogical128BitLane(targetV, 4); targetV = Sse42.Insert(targetV, (short)targetPtr[columnIndex], 0); //left = Sse42.Insert(left, rowIndex + columnIndex + 1, (byte)columnIndex); var leftValue = Vector128.Create(rowIndex + columnIndex + 1); left = Sse42.Or(Sse42.And(shift, leftValue), left); shift = Sse42.ShiftLeftLogical128BitLane(shift, 4); // compare source to target // alternativ, compare equal and OR with One var match = Sse.CompareNotEqual(sourceV.AsSingle(), targetV.AsSingle()); var next = Sse42.Subtract(diag, match.AsInt32()); // Create next diag which is current up var up = Sse42.ShiftLeftLogical128BitLane(left, 4); up = Sse42.Insert(up, previousRowPtr[columnIndex], 0); var tmp = Sse42.Add(Sse42.Min(left, up), one); next = Sse42.Min(next, tmp); left = next; diag = up; } previousRowPtr[0] = Sse42.Extract(left, 3); for (int columnIndex = 4; columnIndex < targetLength; columnIndex++) { // Shift in the next character targetV = Sse42.ShiftLeftLogical128BitLane(targetV, 4); targetV = Sse42.Insert(targetV, (short)targetPtr[columnIndex], 0); // compare source to target // alternativ, compare equal and OR with One var match = Sse42.CompareNotEqual(sourceV.AsSingle(), targetV.AsSingle()); var next = Sse42.Subtract(diag, match.AsInt32()); // Create next diag which is current up var up = Sse42.ShiftLeftLogical128BitLane(left, 4); up = Sse42.Insert(up, previousRowPtr[columnIndex], 0); var tmp = Sse42.Add(Sse42.Min(left, up), one); next = Sse42.Min(next, tmp); left = next; diag = up; // Store one value previousRowPtr[columnIndex - 3] = Sse42.Extract(next, 3); } // Finish with last 3 items, dont read any more chars just extract them for (int i = targetLength - 3; i < targetLength; i++) { // Shift in the next character targetV = Sse42.ShiftLeftLogical128BitLane(targetV, 4); // compare source to target // alternativ, compare equal and OR with One var match = Sse.CompareNotEqual(sourceV.AsSingle(), targetV.AsSingle()); var next = Sse42.Subtract(diag, match.AsInt32()); // Create next diag which is current up var up = Sse42.ShiftLeftLogical128BitLane(left, 4); var tmp = Sse42.Add(Sse42.Min(left, up), one); next = Sse42.Min(next, tmp); left = next; diag = up; // Store one value previousRowPtr[i] = Sse42.Extract(next, 3); } #if DEBUG if (true) { Console.Write("prev values for row {0}:", rowIndex); for (int i = 0; i < targetLength; ++i) { Console.Write("{0} ", previousRow[i]); } Console.WriteLine(); } #endif } //Calculate Single Rows for (; rowIndex < sourceLength; rowIndex++) { var lastSubstitutionCost = rowIndex; var lastInsertionCost = rowIndex + 1; var sourcePrevChar = source[rowIndex]; #if DEBUG Console.Write("prev values for row {0}:", rowIndex); for (int i = 0; i < targetLength; ++i) { Console.Write("{0} ", previousRow[i]); } Console.WriteLine(); #endif CalculateRow(previousRowPtr, targetPtr, targetLength, sourcePrevChar, lastInsertionCost, lastSubstitutionCost); } } var result = previousRow[targetLength - 1]; arrayPool.Return(pooledArray); return(result); }