Is the string ctor the fastest way to convert an IEnumerable to string

后端 未结 2 1356
长情又很酷
长情又很酷 2020-12-10 05:34

Edited for the release of .Net Core 2.1

Repeating the test for the release of .Net Core 2.1, I get results like this

1000000 iter

相关标签:
2条回答
  • 2020-12-10 05:38

    Well, I just wrote up a little test, trying 3 different ways of creating a string from an IEnumerable:

    1. using StringBuilder and repeated invocations of its Append(char ch) method.
    2. using string.Concat<T>
    3. using the String constructor.

    10,000 iterations of generating a random 1,000 character sequence and building a string from it, I see the following timings in a release build:

    • Style=StringBuilder elapsed time is 00:01:05.9687330 minutes.
    • Style=StringConcatFunction elapsed time is 00:02:33.2672485 minutes.
    • Style=StringConstructor elapsed time is 00:04:00.5559091 minutes.

    StringBuilder the clear winner. I'm using a static StringBuilder (singleton) instance, though. Dunno if that makes much difference.

    Here's the source code:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Text;
    
    namespace ConsoleApplication6
    {
      class Program
      {
    
        static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create() ;
    
        static readonly byte[] buffer = {0,0} ;
    
        static char RandomChar()
        {
          ushort codepoint ;
          do
          {
            Random.GetBytes(buffer) ;
            codepoint = BitConverter.ToChar(buffer,0) ;
            codepoint &= 0x007F ; // restrict to Unicode C0 ;
          } while ( codepoint < 0x0020 ) ;
          return (char) codepoint ;
        }
    
        static IEnumerable<char> GetRandomChars( int count )
        {
          if ( count < 0 ) throw new ArgumentOutOfRangeException("count") ;
    
          while ( count-- >= 0 )
          {
            yield return RandomChar() ;
          }
        }
    
        enum Style
        {
          StringBuilder = 1 ,
          StringConcatFunction = 2 ,
          StringConstructor = 3 ,
        }
    
        static readonly StringBuilder sb = new StringBuilder() ;
        static string MakeString( Style style )
        {
          IEnumerable<char> chars = GetRandomChars(1000) ;
          string instance ;
          switch ( style )
          {
          case Style.StringConcatFunction :
            instance = String.Concat<char>( chars ) ;
            break ;
          case Style.StringBuilder : 
            foreach ( char ch in chars )
            {
              sb.Append(ch) ;
            }
            instance = sb.ToString() ;
            break ;
          case Style.StringConstructor :
            instance = new String( chars.ToArray() ) ;
            break ;
          default :
            throw new InvalidOperationException() ;
          }
          return instance ;
        }
    
        static void Main( string[] args )
        {
          Stopwatch stopwatch = new Stopwatch() ;
    
          foreach ( Style style in Enum.GetValues(typeof(Style)) )
          {
            stopwatch.Reset() ;
            stopwatch.Start() ;
            for ( int i = 0 ; i < 10000 ; ++i )
            {
              MakeString( Style.StringBuilder ) ;
            }
            stopwatch.Stop() ;
            Console.WriteLine( "Style={0}, elapsed time is {1}" ,
              style ,
              stopwatch.Elapsed
              ) ;
          }
          return ;
        }
      }
    }
    
    0 讨论(0)
  • 2020-12-10 05:44

    It's worth noting that these results, whilst true for the case of IEnumerable from a purists point of view, are not always thus. For example if you were to actually have a char array even if you are passed it as an IEnumerable it is faster to call the string constructor.

    The results:

    Sending String as IEnumerable<char> 
    10000 iterations of "new string" took 157ms. 
    10000 iterations of "sb inline" took 150ms. 
    10000 iterations of "string.Concat" took 237ms.
    ======================================== 
    Sending char[] as IEnumerable<char> 
    10000 iterations of "new string" took 10ms.
    10000 iterations of "sb inline" took 168ms.
    10000 iterations of "string.Concat" took 273ms.
    

    The Code:

    static void Main(string[] args)
    {
        TestCreation(10000, 1000);
        Console.ReadLine();
    }
    
    private static void TestCreation(int iterations, int length)
    {
        char[] chars = GetChars(length).ToArray();
        string str = new string(chars);
        Console.WriteLine("Sending String as IEnumerable<char>");
        TestCreateMethod(str, iterations);
        Console.WriteLine("===========================================================");
        Console.WriteLine("Sending char[] as IEnumerable<char>");
        TestCreateMethod(chars, iterations);
        Console.ReadKey();
    }
    
    private static void TestCreateMethod(IEnumerable<char> testData, int iterations)
    {
        TestFunc(chars => new string(chars.ToArray()), testData, iterations, "new string");
        TestFunc(chars =>
        {
            var sb = new StringBuilder();
            foreach (var c in chars)
            {
                sb.Append(c);
            }
            return sb.ToString();
        }, testData, iterations, "sb inline");
        TestFunc(string.Concat, testData, iterations, "string.Concat");
    }
    
    0 讨论(0)
提交回复
热议问题