C# 在.NET上更快地解析数字
我已经编写了两个函数,它们将一串空格分隔的整数转换为整数数组。第一个函数使用C# 在.NET上更快地解析数字,c#,performance,parsing,f#,C#,Performance,Parsing,F#,我已经编写了两个函数,它们将一串空格分隔的整数转换为整数数组。第一个函数使用Substring,然后应用System.Int32.Parse将子字符串转换为int值: let intsOfString(s:string)= 设ints=ResizeArray() 让rec进入i j= 如果j=s.长度,则 ints.Add(s.Substring(i,j-i)|>System.Int32.Parse) 其他的 设c=s[j] 如果“0”不确定这是否有用,但您是否尝试过类似的方法: var str
Substring
,然后应用System.Int32.Parse
将子字符串转换为int
值:
let intsOfString(s:string)=
设ints=ResizeArray()
让rec进入i j=
如果j=s.长度,则
ints.Add(s.Substring(i,j-i)|>System.Int32.Parse)
其他的
设c=s[j]
如果“0”不确定这是否有用,但您是否尝试过类似的方法:
var stringValues = input.split(" ");
var intValues = Array.ConvertAll(stringValues, s => int.Parse(s));
我为double编写了这个函数,它不会创建临时子字符串。这意味着要在JSON解析器中使用,因此它将自己限制在如何根据需要在JSON中表示double
它还不是最优的,因为它要求您知道数字的开始和结束位置(begin
和end
参数),因此您必须遍历数字的长度两次才能找到数字的结束位置。它仍然比double.Parse快10-15倍左右,并且可以很容易地修改它,在函数中找到end
,然后作为out
参数返回,以知道必须在哪里继续解析主字符串
这样使用:
Parsers.TryParseDoubleFastStream("1", 0, 1, out j);
Parsers.TryParseDoubleFastStream("2.0", 0, 3, out j);
Parsers.TryParseDoubleFastStream("3.5", 0, 3, out j);
Parsers.TryParseDoubleFastStream("-4.5", 0, 4, out j);
Parsers.TryParseDoubleFastStream("50.06", 0, 5, out j);
Parsers.TryParseDoubleFastStream("1000.65", 0, 7, out j);
Parsers.TryParseDoubleFastStream("-10000.8600", 0, 11, out j);
代码可在此处找到:
(将太长,无法在此发布)
和StandardFunctions.IgnoreChar
对于我来说非常简单:
public static bool IgnoreChar(char c)
{
return c < 33;
}
公共静态bool IgnoreChar(char c)
{
返回c<33;
}
System.Int32.Parse
最慢,因为它使用了CultureInfo
、FormatInfo
等;而性能原因不在临时字符串中
来自反射的代码:
private unsafe static bool ParseNumber(ref char* str, NumberStyles options, ref Number.NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
{
number.scale = 0;
number.sign = false;
string text = null;
string text2 = null;
string str2 = null;
string str3 = null;
bool flag = false;
string str4;
string str5;
if ((options & NumberStyles.AllowCurrencySymbol) != NumberStyles.None)
{
text = numfmt.CurrencySymbol;
if (numfmt.ansiCurrencySymbol != null)
{
text2 = numfmt.ansiCurrencySymbol;
}
str2 = numfmt.NumberDecimalSeparator;
str3 = numfmt.NumberGroupSeparator;
str4 = numfmt.CurrencyDecimalSeparator;
str5 = numfmt.CurrencyGroupSeparator;
flag = true;
}
else
{
str4 = numfmt.NumberDecimalSeparator;
str5 = numfmt.NumberGroupSeparator;
}
int num = 0;
char* ptr = str;
char c = *ptr;
while (true)
{
if (!Number.IsWhite(c) || (options & NumberStyles.AllowLeadingWhite) == NumberStyles.None || ((num & 1) != 0 && ((num & 1) == 0 || ((num & 32) == 0 && numfmt.numberNegativePattern != 2))))
{
bool flag2;
char* ptr2;
if ((flag2 = (((options & NumberStyles.AllowLeadingSign) == NumberStyles.None) ? false : ((num & 1) == 0))) && (ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null)
{
num |= 1;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if (flag2 && (ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null)
{
num |= 1;
number.sign = true;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if (c == '(' && (options & NumberStyles.AllowParentheses) != NumberStyles.None && (num & 1) == 0)
{
num |= 3;
number.sign = true;
}
else
{
if ((text == null || (ptr2 = Number.MatchChars(ptr, text)) == null) && (text2 == null || (ptr2 = Number.MatchChars(ptr, text2)) == null))
{
break;
}
num |= 32;
text = null;
text2 = null;
ptr = ptr2 - (IntPtr)2 / 2;
}
}
}
}
c = *(ptr += (IntPtr)2 / 2);
}
int num2 = 0;
int num3 = 0;
while (true)
{
if ((c >= '0' && c <= '9') || ((options & NumberStyles.AllowHexSpecifier) != NumberStyles.None && ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))))
{
num |= 4;
if (c != '0' || (num & 8) != 0)
{
if (num2 < 50)
{
number.digits[(IntPtr)(num2++)] = c;
if (c != '0' || parseDecimal)
{
num3 = num2;
}
}
if ((num & 16) == 0)
{
number.scale++;
}
num |= 8;
}
else
{
if ((num & 16) != 0)
{
number.scale--;
}
}
}
else
{
char* ptr2;
if ((options & NumberStyles.AllowDecimalPoint) != NumberStyles.None && (num & 16) == 0 && ((ptr2 = Number.MatchChars(ptr, str4)) != null || (flag && (num & 32) == 0 && (ptr2 = Number.MatchChars(ptr, str2)) != null)))
{
num |= 16;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if ((options & NumberStyles.AllowThousands) == NumberStyles.None || (num & 4) == 0 || (num & 16) != 0 || ((ptr2 = Number.MatchChars(ptr, str5)) == null && (!flag || (num & 32) != 0 || (ptr2 = Number.MatchChars(ptr, str3)) == null)))
{
break;
}
ptr = ptr2 - (IntPtr)2 / 2;
}
}
c = *(ptr += (IntPtr)2 / 2);
}
bool flag3 = false;
number.precision = num3;
number.digits[(IntPtr)num3] = '\0';
if ((num & 4) != 0)
{
if ((c == 'E' || c == 'e') && (options & NumberStyles.AllowExponent) != NumberStyles.None)
{
char* ptr3 = ptr;
c = *(ptr += (IntPtr)2 / 2);
char* ptr2;
if ((ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null)
{
c = *(ptr = ptr2);
}
else
{
if ((ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null)
{
c = *(ptr = ptr2);
flag3 = true;
}
}
if (c >= '0' && c <= '9')
{
int num4 = 0;
do
{
num4 = num4 * 10 + (int)(c - '0');
c = *(ptr += (IntPtr)2 / 2);
if (num4 > 1000)
{
num4 = 9999;
while (c >= '0' && c <= '9')
{
c = *(ptr += (IntPtr)2 / 2);
}
}
}
while (c >= '0' && c <= '9');
if (flag3)
{
num4 = -num4;
}
number.scale += num4;
}
else
{
ptr = ptr3;
c = *ptr;
}
}
while (true)
{
if (!Number.IsWhite(c) || (options & NumberStyles.AllowTrailingWhite) == NumberStyles.None)
{
bool flag2;
char* ptr2;
if ((flag2 = (((options & NumberStyles.AllowTrailingSign) == NumberStyles.None) ? false : ((num & 1) == 0))) && (ptr2 = Number.MatchChars(ptr, numfmt.positiveSign)) != null)
{
num |= 1;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if (flag2 && (ptr2 = Number.MatchChars(ptr, numfmt.negativeSign)) != null)
{
num |= 1;
number.sign = true;
ptr = ptr2 - (IntPtr)2 / 2;
}
else
{
if (c == ')' && (num & 2) != 0)
{
num &= -3;
}
else
{
if ((text == null || (ptr2 = Number.MatchChars(ptr, text)) == null) && (text2 == null || (ptr2 = Number.MatchChars(ptr, text2)) == null))
{
break;
}
text = null;
text2 = null;
ptr = ptr2 - (IntPtr)2 / 2;
}
}
}
}
c = *(ptr += (IntPtr)2 / 2);
}
if ((num & 2) == 0)
{
if ((num & 8) == 0)
{
if (!parseDecimal)
{
number.scale = 0;
}
if ((num & 16) == 0)
{
number.sign = false;
}
}
str = ptr;
return true;
}
}
str = ptr;
return false;
}
public static int Parse(string s)
{
return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
internal unsafe static int ParseInt32(string s, NumberStyles style, NumberFormatInfo info)
{
byte* stackBuffer = stackalloc byte[1 * 114 / 1];
Number.NumberBuffer numberBuffer = new Number.NumberBuffer(stackBuffer);
int result = 0;
Number.StringToNumber(s, style, ref numberBuffer, info, false);
if ((style & NumberStyles.AllowHexSpecifier) != NumberStyles.None)
{
if (!Number.HexNumberToInt32(ref numberBuffer, ref result))
{
throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
}
}
else
{
if (!Number.NumberToInt32(ref numberBuffer, ref result))
{
throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
}
}
return result;
}
private unsafe static void StringToNumber(string str, NumberStyles options, ref Number.NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
{
if (str == null)
{
throw new ArgumentNullException("String");
}
fixed (char* ptr = str)
{
char* ptr2 = ptr;
if (!Number.ParseNumber(ref ptr2, options, ref number, info, parseDecimal) || ((ptr2 - ptr / 2) / 2 < str.Length && !Number.TrailingZeros(str, (ptr2 - ptr / 2) / 2)))
{
throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
}
}
}
private不安全静态bool ParseNumber(ref char*str,numberstyle选项,ref Number.NumberBuffer Number,NumberFormatInfo numfmt,bool parseDecimal)
{
数字。比例=0;
number.sign=false;
字符串文本=空;
字符串text2=null;
字符串str2=null;
字符串str3=null;
布尔标志=假;
字符串str4;
字符串str5;
if((选项和NumberStyles.AllowCurrencySymbol)!=NumberStyles.None)
{
text=numfmt.CurrencySymbol;
如果(numfmt.ansiCurrencySymbol!=null)
{
text2=numfmt.ansiCurrencySymbol;
}
str2=numfmt.NumberDecimalSeparator;
str3=numfmt.NumberGroupSeparator;
str4=numfmt.CurrencyDecimalSeparator;
str5=numfmt.CurrencyGroupSeparator;
flag=true;
}
其他的
{
str4=numfmt.NumberDecimalSeparator;
str5=numfmt.NumberGroupSeparator;
}
int num=0;
char*ptr=str;
字符c=*ptr;
while(true)
{
如果(!Number.IsWhite(c)|(options&NumberStyles.allowreadingwhite)==NumberStyles.None | |((num&1)!=0&((num&1)==0 | |((num&32)==0&&numfmt.numberNegativePattern!=2)))
{
布尔flag2;
char*ptr2;
如果((flag2=((options&NumberStyles.allowReadingSign)==NumberStyles.None)?false:((num&1)==0))&&(ptr2=Number.MatchChars(ptr,numfmt.positiveSign))!=null)
{
num |=1;
ptr=ptr2-(IntPtr)2/2;
}
其他的
{
if(flag2&(ptr2=Number.MatchChars(ptr,numfmt.negativeSign))!=null)
{
num |=1;
number.sign=true;
ptr=ptr2-(IntPtr)2/2;
}
其他的
{
如果(c='('&&(options&NumberStyles.AllowParentheses)!=NumberStyles.None&&(num&1)==0)
{
num |=3;
number.sign=true;
}
其他的
{
if((text==null | | |(ptr2=Number.MatchChars(ptr,text))==null)和&(text2==null | |(ptr2=Number.MatchChars(ptr,text2))==null))
{
打破
}
num |=32;
text=null;
text2=null;
ptr=ptr2-(IntPtr)2/2;
}
}
}
}
c=*(ptr+=(IntPtr)2/2);
}
int num2=0;
int num3=0;
while(true)
{
如果((c>='0'&&c='a'&&c='a'&&c='0'&&c 1000)
{
num4=9999;
而(c>='0'&&c='0'&&c将所有代码粘贴到c#中,并调用Test()
。这是使用C#直接在字符串数组上操作以解析数字的最接近的方法。它是为速度而不是优雅而构建的。ParseInt
和ParseFloat
函数是为OpenGL图形引擎创建的,用于从基于文本的3d模型导入向量。解析浮点是这方面的一个重要瓶颈过程。这是我能做的最快的
using System.Diagnostics;
private void Test()
{
Stopwatch sw = new Stopwatch();
StringBuilder sb = new StringBuilder();
int iterations = 1000;
// Build a string of 1000000 space separated numbers
for (var n = 0; n < iterations; n++)
{
if (n > 0)
sb.Append(' ');
sb.Append(n.ToString());
}
string numberString = sb.ToString();
// Time the process
sw.Start();
StringToInts(numberString, iterations);
//StringToFloats(numberString, iterations);
sw.Stop();
long proc1 = sw.ElapsedMilliseconds;
Console.WriteLine("iterations: {0} \t {1}ms", iterations, proc1);
}
private unsafe int[] StringToInts(string s, int length)
{
int[] ints = new int[length];
int index = 0;
int startpos = 0;
fixed (char* pStringBuffer = s)
{
fixed (int* pIntBuffer = ints)
{
for (int n = 0; n < s.Length; n++)
{
if (s[n] == ' ' || n == s.Length - 1)
{
if (n == s.Length - 1)
n++;
// pIntBuffer[index++] = int.Parse(new string(pStringBuffer, startpos, n - startpos));
pIntBuffer[index++] = ParseInt((pStringBuffer + startpos), n - startpos);
startpos = n + 1;
}
}
}
}
return ints;
}
private unsafe float[] StringToFloats(string s, int length)
{
float[] floats = new float[length];
int index = 0;
int startpos = 0;
fixed (char* pStringBuffer = s)
{
fixed (float* pFloatBuffer = floats)
{
for (int n = 0; n < s.Length; n++)
{
if (s[n] == ' ' || n == s.Length - 1)
{
if (n == s.Length - 1)
n++;
pFloatBuffer[index++] = ParseFloat((pStringBuffer + startpos), n - startpos); // int.Parse(new string(pStringBuffer, startpos, n - startpos));
startpos = n + 1;
}
}
}
}
return floats;
}
public static unsafe int ParseInt(char* input, int len)
{
int pos = 0; // read pointer position
int part = 0; // the current part (int, float and sci parts of the number)
bool neg = false; // true if part is a negative number
int* ret = stackalloc int[1];
while (pos < len && (*(input + pos) > '9' || *(input + pos) < '0') && *(input + pos) != '-')
pos++;
// sign
if (*(input + pos) == '-')
{
neg = true;
pos++;
}
// integer part
while (pos < len && !(input[pos] > '9' || input[pos] < '0'))
part = part * 10 + (input[pos++] - '0');
*ret = neg ? (part * -1) : part;
return *ret;
}
public static unsafe float ParseFloat(char* input, int len)
{
//float ret = 0f; // return value
int pos = 0; // read pointer position
int part = 0; // the current part (int, float and sci parts of the number)
bool neg = false; // true if part is a negative number
float* ret = stackalloc float[1];
// find start
while (pos < len && (input[pos] < '0' || input[pos] > '9') && input[pos] != '-' && input[pos] != '.')
pos++;
// sign
if (input[pos] == '-')
{
neg = true;
pos++;
}
// integer part
while (pos < len && !(input[pos] > '9' || input[pos] < '0'))
part = part * 10 + (input[pos++] - '0');
*ret = neg ? (float)(part * -1) : (float)part;
// float part
if (pos < len && input[pos] == '.')
{
pos++;
double mul = 1;
part = 0;
while (pos < len && !(input[pos] > '9' || input[pos] < '0'))
{
part = part * 10 + (input[pos] - '0');
mul *= 10;
pos++;
}
if (neg)
*ret -= (float)part / (float)mul;
else
*ret += (float)part / (float)mul;
}
// scientific part
if (pos < len && (input[pos] == 'e' || input[pos] == 'E'))
{
pos++;
neg = (input[pos] == '-'); pos++;
part = 0;
while (pos < len && !(input[pos] > '9' || input[pos] < '0'))
{
part = part * 10 + (input[pos++] - '0');
}
if (neg)
*ret /= (float)Math.Pow(10d, (double)part);
else
*ret *= (float)Math.Pow(10d, (double)part);
}
return (float)*ret;
}
使用系统诊断;
专用无效测试()
{
秒表sw=新秒表();
StringBuilder sb=新的StringBuilder();
int迭代次数=1000次;
//构建一个由1000000个空格分隔的数字组成的字符串
对于(var n=0;n0)
某人附加(“”);
某人附加(n.ToString());
}
string numberString=sb.ToString();
//给过程计时
sw.Start();
StringToInts(数字字符串、迭代);
//StringToFloats(numberString,迭代);
sw.Stop();
长proc1=sw.ElapsedMilliseconds;
WriteLine(“迭代:{0}\t{1}ms”,迭代,proc1);
}
私有不安全int[]StringToInts(字符串s,int长度)
{
int[]ints=新的int[长度];
int指数=0;
int startpos=0;
F