2013-07-01 3 views
2

Я задавался вопросом, есть ли какое-либо влияние на производительность, когда я выбираю добавить одну строку символов или один символ в константу.Производительность объединения строки и char

Так что я написал небольшой консоли Application: (.NET 4)

static class Program 
{ 
    const string STR = "string "; 

    static void Main() 
    { 
     var arr = new string[99999999]; 

     Stopwatch timer = new Stopwatch(); 

     Console.ReadLine(); 

     timer.Start(); 

     //for (uint i = 0; i < 99999999; i++) 
     //{ 
     // arr[i] = STR + 'C'; 
     //} 

     for (uint i = 0; i < 99999999; i++) 
     { 
      arr[i] = STR + "C"; 
     } 

     timer.Stop(); 

     Console.WriteLine(timer.ElapsedMilliseconds); 

     Console.ReadLine(); 
    } 
} 

Вы должны прокомментировать один для цикла.

Итак, STR + "C" занимает около 1300 мс.

Для STR + 'C' Я еще не видел результата. Это занимает слишком много времени и, похоже, сильно беспокоит мой компьютер.

Итак, мой вопрос. Каким образом это влияние на производительность возможно? Я знаю, что массив значений 99999999 не будет встречаться очень часто в практическом использовании, но это все еще огромная разница.

Заранее благодарен!

ответ

6

Это на самом деле очень просто объяснить: вы наткнулись на то, что компилятор C# будет выполнять постоянный складной на строковых выражениях.

Поскольку вы объявили STR равным const, это приведет к замене ссылок на него литеральной строкой "string ". Затем, когда компилятор встречается с "string " + "C", он заменяет это выражение эквивалентом "string C". Таким образом, цикл, который на самом деле заканчивается, тратит все свое время, назначая эту строку различным позициям в массиве.

Наоборот, char конкатенация не оптимизированы таким образом, чтобы вы на самом деле придется ждать как конкатенация (включая выделение нового string объекта), а также присвоение массива. Кроме того, цикл будет генерировать тонну мусора, так что вы также ожидаете коллектора.

Если вы хотите сравнить две операции честно, я хотел бы сделать две вещи:

  1. Измените декларацию STR в static readonly вместо const.
  2. Уменьшите количество итераций, чтобы вы могли получить полный прогон.
+0

Спасибо, отличный ответ! Даже без константы 'STR +" C "' почти в два раза быстрее добавления символа. Поэтому я буду использовать одиночные строки символов. Еще раз спасибо! – Andy

+0

@ Энди Рад помочь. Ваши наблюдения имеют смысл, поскольку '' '' '' '' '' '' непосредственно можно конкатенировать, вместо того, чтобы сначала преобразовать 'char' в' string'. – dlev

+0

@ Andy Это имеет смысл в сочетании с ответом Тиграна: оператор '+' заменяется компилятором C# с вызовом 'String.Concat'. Когда вы используете его со строкой, он может напрямую вызвать перегрузку, которая принимает строковые параметры. Когда вы используете его с символом, он должен вставить символ и вызвать перегрузку, которая принимает параметр Object. Операции бокса и распаковки относительно дороги, особенно когда они выполняются в замкнутом цикле. Но будьте осторожны, это микро-оптимизация на пике! –

4

Простая программа, как это:

var val = "hello ";  
val += 'r'; 

выполняет бокс char значения для object, который мы можем видеть из сгенерированного IL

IL_0001: ldstr  "hello " 
IL_0006: stloc.0  // val 
IL_0007: ldloc.0  // val 
IL_0008: ldc.i4.s 72 
IL_000A: box   System.Char 
IL_000F: call  System.String.Concat 
IL_0014: stloc.0  // val 
IL_0015: ldloc.0  // val 

Вместо в случае string, нет какого-либо бокс, так что это значительно быстрее.

Так почему же бокс выполнен? Потому что вызывается System.String.Concat(String,String) (то есть результат вызова оператора binary +) по двум аргументам, где только один из них - string, вызывает перегрузку String.Concat(object,object), поэтому значение char помещается в коробку, чтобы иметь возможность перейти в этот вызов метода.

+1

Вы забыли объяснить * почему * символ в коробке, но строки нет. –

+0

@CodyGray: отредактировал мой пост. – Tigran

Смежные вопросы