zoukankan      html  css  js  c++  java
  • [C#].NET中字符串的 ToLower,ToUpper,ToLowerInvariant,ToUpperInvariant 性能测试及分析

    我最近在研读《CLR via C#》,其中有一个章节讲String类型,Jeffrey说当进行不区分大小写的字符串对比时,应当尽量多的使用ToUpperInvariant,因为一方面CLR对转换为大写的操作进行了专门的优化,使其性能更加卓越;另一方面ToUpperInvariant和ToLowerInvariant忽略了对语言文化的处理,因此性能比ToUpper和ToLower要快得多。

    从理论层面上来讲,这应该是正确的。但是究竟能快多少呢?或者说这种性能提升值不值得我们付出专门的精力来关注呢?

    我们下面就进行一个测试,看看它们究竟能对性能产生多大影响。

    首先,我实例化了一个 char 类型数组,并使用 Char Code 在 0 到 133 之间的随机字符填满,我们就得到了一个随机的测试用例,其中包含半角符号、小写英文字母和大写英文字母。

    然后我要分别测试 Char 类型的四个静态函数和 String 类型的四个实例函数,我猜测 String 类型中的实例函数实际上也是通过 Char 类型中相对应的静态函数实现的。

    为了避免CLR的内存优化对测试结果产生影响,每执行一项测试之前,都将在堆中创建新的示例,并从测试用例中复制成员。

    除此之外,为了尽量保证测试结果的正确性,我将循环执行并记录测试代码 N 次,之后取它们的去权平均值。

    =====================我是分割线=====================

    下面是测试代码:

    填充数组:

     1 static void Fill()
     2  {
     3     //实例化一个容纳10000个字符的数组
     4     letters = new char[maxElementsCount];
     5     //填充数组
     6     for (int i = 0; i < maxElementsCount; i++)
     7     {
     8         var charcode = R.Next(0, 133);
     9         letters[i] = Convert.ToChar(charcode);
    10     }
    11 }

    执行 Char 静态方法测试的执行函数:

     1  static double Invoke(Action<char[]> func,string testname)
     2 {
     3     char[] source = new char[maxElementsCount];
     4     letters.CopyTo(source, 0);
     5     DateTime start = DateTime.Now; //当前时间
     6     DateTime end = DateTime.Now;
     7     Console.WriteLine("开始测试 "+ testname + " [开始时间:" + start.ToString() + "]");
     8     func(source);
     9     end = DateTime.Now;
    10     var during = (end - start).TotalMilliseconds;
    11     Console.WriteLine("测试 " + testname + " 完成,耗时" + during + "毫秒[结束时间:" + end.ToString() + "]");
    12     return during;
    13 }

    执行 String 实例方法的执行函数:

     1 static double InvokeString(Action<string> func, string testname)
     2 {
     3     string source = new string(letters);
     4     DateTime start = DateTime.Now; //当前时间
     5     DateTime end = DateTime.Now;
     6     Console.WriteLine("开始测试 " + testname + " [开始时间:" + start.ToString() + "]");
     7     func(source);
     8     end = DateTime.Now;
     9     var during = (end - start).TotalMilliseconds;
    10     Console.WriteLine("测试 " + testname + " 完成,耗时" + during + "毫秒[结束时间:" + end.ToString() + "]");
    11     return during;
    12 }

    测试方法:

     1 static void Run(int maxElementsCount)
     2 {
     3     Fill();
     4 
     5     char_tolower.Add(Invoke(s =>
     6     {
     7         for (int i = 0; i < s.Length; i++)
     8         {
     9             char.ToLower(s[i]);
    10         }
    11     }, "Char.ToLower"));
    12 
    13     char_toupper.Add(Invoke(s =>
    14     {
    15         for (int i = 0; i < s.Length; i++)
    16         {
    17             char.ToUpper(s[i]);
    18         }
    19     }, "Char.ToUpper"));
    20 
    21     char_tolowerinvariant.Add(Invoke(s =>
    22     {
    23         for (int i = 0; i < s.Length; i++)
    24         {
    25             char.ToLowerInvariant(s[i]);
    26         }
    27     }, "Char.ToLowerInvariant"));
    28 
    29     char_toupperinvariant.Add(Invoke(s =>
    30     {
    31         for (int i = 0; i < s.Length; i++)
    32         {
    33             char.ToUpperInvariant(s[i]);
    34         }
    35     }, "Char.ToUpperInvariant"));
    36 
    37     string_tolower.Add(InvokeString(s =>
    38     {
    39         s.ToLower();
    40     }, "String.ToLower"));
    41 
    42     string_toupper.Add(InvokeString(s =>
    43     {
    44         s.ToUpper();
    45     }, "String.ToUpper"));
    46 
    47     string_tolowerinvariant.Add(InvokeString(s =>
    48     {
    49         s.ToLowerInvariant();
    50     }, "String.ToLowerInvariant"));
    51 
    52     string_toupperinvariant.Add(InvokeString(s =>
    53     {
    54         s.ToUpperInvariant();
    55     }, "String.ToUpperInvariant"));
    56 }

     以下是循环执行1000次的去权平均数:

     从结果中我们可以看出,ToLowerInvariant 和 ToUpperInvariant 因为忽略的语言信息,性能的确是比关注语言信息的 ToLower 和 ToUpper 要快一些。但是Char.ToUpper的性能比Char.ToLower的性能竟然还要差,这可能是由于随机的测试数据不够随机造成的。

    此外,还可以得出 String 类型的四个实例函数并非通过 Char 中的静态函数实现的 , 这应该是因为.NET CLR 中定义的 String 是一个单独的基元类型,而并非是 Char 数组 , 因此经过了单独的实现,并且性能明显要好过 Char 的数组。

    但是我这里用的测试数据是一个长达 100万 字的字符串,在这种情况下的性能损失不足0.1毫秒,实在是不值一提。我测试用的电脑是 i5 4代处理器,相信在计算能力更强的处理器上,这个数值将被无限的缩小。因此我们在实际开发中几乎不需要特别关注应当用哪个方法,特别是如果在做国际化的应用时,ToLowerInvariant 和 ToUpperInvariant 虽然能够带来性能的提升,但是也可能导致隐形BUG,并不值得推广。此外,在开发中我们应当尽量少的使用 Char 类型,一个 Char 类型对象要单独占用一个栈内存地址,且只能存储一个字符,实在是太不合算。

  • 相关阅读:
    mysql的常用查询创建命令
    maven的简介
    google guava
    分库分表的情况下生成全局唯一的ID
    书单
    MD5Util
    UUID生成工具
    nodejs学习笔记三——nodejs使用富文本插件ueditor
    nodejs学习笔记二——链接mongodb
    mongodb 安装
  • 原文地址:https://www.cnblogs.com/TO-WW/p/6118700.html
Copyright © 2011-2022 走看看