I think your implementation is pretty decent. I've made an unsafe version using pointer math, which is indeed faster, but is dirtier in my opinion.
Results (running 30 samples of 10M random int strings):
Int32.Parse 1766.17 ms (stddev: 1.83 ms)
Numbers.Int32.ParseTrusted 262.07 ms (stddev: 0.44 ms)
ParseTrustedUnsafe 146.13 ms (stddev: 0.43 ms)
here is my unsafe implementation:
public static unsafe int ParseTrustedUnsafe(string str, int start, int end)
{
unsafe
{
Int32 result = 0;
Int32 length = end - start;
Boolean isNegative = false;
fixed (Char* startChar = str)
{
Byte* currentChar = ((Byte*)startChar) + (start * 2);
if (*currentChar == 0x2D)
{
isNegative = true;
currentChar += 2;
length--;
}
else if (*currentChar == 0x2B)
{
currentChar += 2;
length--;
}
while (length >= 4)
{
result = (result * 10) + (*currentChar - 0x30);
result = (result * 10) + (*(currentChar + 2) - 0x30);
result = (result * 10) + (*(currentChar + 4) - 0x30);
result = (result * 10) + (*(currentChar + 6) - 0x30);
currentChar += 8;
length -= 4;
}
while (length > 0)
{
result = (result * 10) + (*currentChar - 0x30);
currentChar += 2;
length -= 1;
}
}
return isNegative ? -result : result;
}
}
Below is my validation and benchmark program:
static void Main(string[] args)
{
String[] testValues = new String[10 * 100 * 100 * 100];
Random rand = new Random();
for (int i = 0; i < testValues.Length; i++)
{
testValues[i] = rand.Next().ToString();
}
// validate
for (int i = 0; i < testValues.Length; i++)
{
Debug.Assert(String.CompareOrdinal(testValues[i], ParseTrustedUnsafe(testValues[i], 0, testValues[i].Length).ToString()) == 0, "ParseTrustedUnsafe failed on " + testValues[i]);
Debug.Assert(String.CompareOrdinal(testValues[i], Numbers.Int32.ParseTrusted(testValues[i], 0, testValues[i].Length).ToString()) == 0, "Numbers.Int32.ParseTrusted failed on " + testValues[i]);
}
#if DEBUG
return;
#endif
List<List<Double>> results = new List<List<double>>();
for (int i = 0; i < 3; i++)
{
results.Add(new List<double>());
}
Thread.CurrentThread.Priority = ThreadPriority.Highest;
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
if (Environment.ProcessorCount > 1)
{
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1 << (Environment.ProcessorCount - 1)); // use last core only
}
Stopwatch sw = new Stopwatch();
for (int testrun = 0; testrun < 30; testrun++)
{
sw.Reset();
sw.Start();
for (int i = 0; i < testValues.Length; i++)
{
Int32.Parse(testValues[i]);
}
sw.Stop();
results[0].Add(sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for (int i = 0; i < testValues.Length; i++)
{
Numbers.Int32.ParseTrusted(testValues[i]);
}
sw.Stop();
results[1].Add(sw.ElapsedMilliseconds);
sw.Reset();
sw.Start();
for (int i = 0; i < testValues.Length; i++)
{
ParseTrustedUnsafe(testValues[i], 0, testValues[0].Length);
}
sw.Stop();
results[2].Add(sw.ElapsedMilliseconds);
}
Console.WriteLine("Int32.Parse \t\t\t {0:0.00}ms (stddev: {1:0.00}ms)", results[0].Average(), StandardDeviation(results[0]));
Console.WriteLine("Numbers.Int32.ParseTrusted \t {0:0.00}ms (stddev: {1:0.00}ms)", results[1].Average(), StandardDeviation(results[1]));
Console.WriteLine("ParseTrustedUnsafe \t\t {0:0.00}ms (stddev: {1:0.00}ms)", results[2].Average(), StandardDeviation(results[2]));
}
private static double StandardDeviation(IEnumerable<double> doubleList)
{
double average = doubleList.Average();
double sumOfDerivation = 0;
foreach (double value in doubleList)
{
sumOfDerivation += (value) * (value);
}
double sumOfDerivationAverage = sumOfDerivation / doubleList.Count();
return Math.Sqrt(sumOfDerivationAverage - (average * average));
}