代码之家  ›  专栏  ›  技术社区  ›  Hosam Aly

我应该滚动我自己的ParseInt32版本吗?

  •  2
  • Hosam Aly  · 技术社区  · 17 年前

    我正在编写一个高性能解析器,在我看来 Int32.Parse 可能太慢。我写了一个简单的版本,假设输入正确,它的性能要好得多。那么,我应该创建自己的版本吗?还是已经有了另一种更快的方法?

    我的方法是这样的:

    // parse simple int, assuming relatively correct input (i.e. all digits)
    public static int ParseInt32Simply(string str) {
        if (str == null) throw new ArgumentNullException("str");
        if (str.Length == 0) throw new ArgumentException("str is empty");
    
        int sign = 1, index = 0;
        if (str[0] == '-') { sign = -1; index = 1; }
        else if (str[0] == '+') { index = 1; }
    
        int result = 0;
        for (; index < str.Length; ++index) {
            result = 10 * result + (str[index] - '0');
        }
    
        if (result < 0) throw new OverflowException(str + " is too large for Int32");
    
        return result * sign;
    }
    

    我的结果与内置的等效结果非常不同:

    Int32.Parse      took 8.2775453 seconds
    ParseInt32Simply took 0.6511523 seconds
    Int32.Parse      took 6.7625807 seconds
    ParseInt32Simply took 0.4677390 seconds
    

    (在我的机器上运行2500万次迭代;P4 3 GHz,运行VS 2008 SP1)

    那么,我应该使用我的版本吗?或者还有其他方法可以使用吗?

    11 回复  |  直到 17 年前
        1
  •  6
  •   Zach Hirsch    17 年前

    您是否已经分析了您的代码,以确定ParseInt32实际上是瓶颈?我不会替换你正在编码的环境的“标准库”的一部分,除非你确信你会看到它的好处。

        2
  •  5
  •   Keith    17 年前

    In.net Int32.Parse 非常 非常 快,当它成功的时候。

    当它失败时,它会抛出一个异常——然后它会非常慢,因为异常很慢。

    你需要扩展你的测试——你需要检查好字符串和坏字符串的模式,这与你需要它做的事情相似。

    如果你非常确定你的所有字符串都是有效的整数,那么 Int32解析 这是我们应该走的路。如果你怀疑任何超过微不足道的几个都是有效的,那么使用它要快得多 Int32.TryParse ,而不是a try-catch 在你的循环中。

    通常,如果你 异常处理机制 是循环外使用 Int32解析 -您将收到一个异常,并在第一次收到无效值时停止。

    如果你 异常处理机制 在循环内使用 Int32.TryParse 相反。

    两者 Int32解析 Int32.TryParse 它们经过了高度优化,相对成熟——我预计除非你有特殊情况,否则它们很难改进。

        3
  •  4
  •   Chris Kimpton    17 年前

    我的观点是,如果你节省的时间很大,对你的申请有好处,那么就去做吧。

    我们在XML解析方面遇到了一个类似的问题,出于性能原因,我们选择手动进行解析,但这是基于一个已知的环境——我们正在馈送XML,所以我们可以相当安全地在解析中走捷径。

    显然,风险在于它不太可能像标准库版本那样完整,因此团队的新开发人员需要意识到这一点,以免他们做些什么来破坏它。

        4
  •  4
  •   Statement    17 年前

    是的,您可以使用自己的解析int版本,只要您100%确定源数据是您可以控制的(因此始终符合您的Int32格式)。此外,您应该使用与世界其他地方隔离的自己的代码,因为如果您在发布的某个库中有此代码,人们可能希望具有Int32.Parse的标准行为。如果你不能提供,那对他们没有好处。然而,正如这里的许多人所建议的那样,如果你想充分发挥自己的表现,你应该确定这是真正需要做的。然而,你可能比这里的任何人都更了解自己的代码。

    就我个人而言,我会尽量避免更改解析。如果还有其他瓶颈,那么这些瓶颈可能值得首先调查。

        5
  •  4
  •   Giovanni Galbo    17 年前

    如果你的测试是可验证的,并且你真的需要性能提升(例如,你每秒调用函数数万次),那么就去做吧。

    我只想改名。..因为 ParseInt32Simply 不告诉维护程序员任何事情。我认为一个名字像 TrustedSourceInt32Parse 保证Int32Parse 或者类似的东西是一个更好的名字。

        6
  •  3
  •   Lasse V. Karlsen    17 年前

    我认为这里的主要问题是你的句子 假设输入正确 。从阅读代码来看,它似乎无法正确处理“12x”。

    Int32.Parse做了很多事情来验证输入,甚至可能会注意到你的文化来处理一些文化差异,尽管我想不出任何专门针对Int32的东西。

    您确定代码中的瓶颈是Int32吗?

        7
  •  1
  •   Rumen Georgiev    17 年前

    如果你解析的是一种你知道是有效数字的格式,你确实可以编写一个更快的自定义解析器。我写了一个双。为同一目的解析函数一次。从最低有效数字开始会更快。这样,您就可以增加解析数字的能力。

    我创建了一个快速实现,

    public static Int32 ParseValidNumberAsInt32(string str)
    {
        if (str == null) 
            throw new ArgumentNullException("str");
        if (str.Length == 0) 
            throw new ArgumentException("str is empty");
        Int32 result = 0;
        Int32 currentPower = 1;
        Boolean isNegative = str[0] == '-';
    
        for (int currentCharIndex = str.Length - 1; currentCharIndex > 0; currentCharIndex--)
        {
            result += (str[currentCharIndex] - '0') * currentPower;
            currentPower *= 10;
        }
        return isNegative ? -1 * result : result + ((str[0] - '0') * currentPower);
    }
    

    如果你真的想要速度,你可以编写一个不安全的实现。。

    如果你解析一个大文件,你可以将文件作为原始字节读取并使用它们。这将使它更快(不转换为unicode字符串,不将字符串拆分为行,不将行拆分为子字符串,不解析子字符串),但你会失去可维护性。

        8
  •  1
  •   dalle    17 年前

    你如何测量速度?我试过这个:

    Stopwatch sw = new Stopwatch();
    Random rand = new Random();
    
    for (int n = 0; n < 10; n++)
    {
        sw.Start();
        for (int i = 0; i < 1000000; i++)
        {
            ParseInt32Simply(rand.Next().ToString());
        }
        sw.Stop();
        Console.WriteLine(sw.Elapsed.Ticks + " - ParseInt32Simply");
        sw.Reset();
    
        sw.Start();
        for (int i = 0; i < 1000000; i++)
        {
            int.Parse(rand.Next().ToString());
        }
        sw.Stop();
        Console.WriteLine(sw.Elapsed.Ticks + " - int.Parse");
        sw.Reset();
        Console.WriteLine();
    }
    

    结果完全不同:

    2932852-ParseInt32Simply
    4684522-内部解析

    3003988-ParseInt32Simply
    4666928-内部解析

    2892545-ParseInt32Simply
    4660209-内部解析

    2888998-ParseInt32Simply
    4636007-内部解析

    2955727-ParseInt32Simply
    4668501-内部解析

    2929210-ParseInt32Simply
    4653799-内部解析

    2893706-ParseInt32Simply
    4671503-内部解析

    2899547-ParseInt32Simply
    4633957-内部解析

    你的简单方法仍然更快,但不到2倍(实际上这是非常好的性能!)。

        9
  •  1
  •   Davy Landman    17 年前

    看看这个博客条目: Fast string to integer conversion 卡尔Seguin。

        10
  •  0
  •   Rumen Georgiev    17 年前

    对null和空字符串的验证是不够的,您应该检查参数是否是有效的整数。

        11
  •  0
  •   Mafti    17 年前

    你的考试怎么样? 看来你的测试不好。

    当我循环50000次时,只有很小的差异 然后我有大约3万个刻度,赞成你的定制方法, 但由于常规方法的优点,这是可以忽略的

    推荐文章