Каков самый быстрый способ перебора отдельных символов строки в C#?C#

Место общения программистов C#
Anonymous
Каков самый быстрый способ перебора отдельных символов строки в C#?

Сообщение Anonymous »

Заголовок – это вопрос. Ниже представлена ​​моя попытка ответить на этот вопрос посредством исследования. Но я не доверяю своим неосведомленным исследованиям, поэтому все же задаю вопрос (Каков самый быстрый способ перебора отдельных символов в строке в C#?).

Иногда Я хочу последовательно перебирать символы строки, например, при анализе вложенных токенов - то, что невозможно сделать с помощью регулярных выражений. Мне интересно, какой самый быстрый способ перебирать отдельные символы строки, особенно очень большие строки.

Я сам провел ряд тестов, и мои результаты приведены ниже. Однако есть много читателей с гораздо более глубокими знаниями о компиляторе .NET CLR и C#, поэтому я не знаю, упускаю ли я что-то очевидное или допустил ошибку в своем тестовом коде. Поэтому я прошу вашего коллективного ответа. Если кто-нибудь знает, как на самом деле работает индексатор строк, это будет очень полезно. (Это функция языка C#, скомпилированная во что-то еще за кулисами? Или что-то встроенное в CLR?).

Первый метод с использованием потока был взят непосредственно из принятого ответа ветки: как сгенерировать поток из строки?
Тесты

Код: Выделить всё

longString
— это строка длиной 99,1 миллиона символов, состоящая из 89 копий текстовой версии спецификации языка C#. Показанные результаты относятся к 20 итерациям. Там, где есть время «запуска» (например, для первой итерации неявно созданного массива в методе № 3), я проверял это отдельно, например, выйдя из цикла после первой итерации.

Результаты

Из моих тестов кэширование строки в массиве символов с использованием метода ToCharArray() является самый быстрый для перебора всей строки. Метод ToCharArray() требует предварительных затрат, а последующий доступ к отдельным символам происходит немного быстрее, чем встроенный метод доступа к индексу.

Код: Выделить всё

                                           milliseconds
---------------------------------
Method                         Startup  Iteration  Total  StdDev
------------------------------  -------  ---------  -----  ------
1 index accessor                     0        602    602       3
2 explicit convert ToCharArray     165        410    582       3
3 foreach (c in string.ToCharArray)168        455    623       3
4 StringReader                       0       1150   1150      25
5 StreamWriter => Stream           405       1940   2345      20
6 GetBytes() => StreamReader       385       2065   2450      35
7 GetBytes() => BinaryReader       385       5465   5850      80
8 foreach (c in string)              0        960    960       4
Обновление: Согласно комментарию @Eric, вот результаты для 100 итераций по более обычной символьной строке размером 1,1 МБ (одна копия спецификации C# ). Индексаторы и массивы символов по-прежнему являются самыми быстрыми, за ними следуют foreach(char in string), а затем потоковые методы.

Код: Выделить всё

                                           milliseconds
---------------------------------
Method                         Startup  Iteration  Total  StdDev
------------------------------  -------  ---------  -----  ------
1 index accessor                     0        6.6    6.6    0.11
2 explicit convert ToCharArray     2.4        5.0    7.4    0.30
3 for(c in string.ToCharArray)     2.4        4.7    7.1    0.33
4 StringReader                       0       14.0   14.0    1.21
5 StreamWriter => Stream           5.3       21.8   27.1    0.46
6 GetBytes() => StreamReader       4.4       23.6   28.0    0.65
7 GetBytes() => BinaryReader       5.0       61.8   66.8    0.79
8 foreach (c in string)              0       10.3   10.3    0.11
Используемый код (проверено отдельно; для краткости показано вместе)

Код: Выделить всё

//1 index accessor
int strLength = longString.Length;
for (int i = 0; i < strLength; i++) { c = longString[i]; }

//2 explicit convert ToCharArray
int strLength = longString.Length;
char[] charArray = longString.ToCharArray();
for (int i = 0; i < strLength; i++) { c = charArray[i]; }

//3 for(c in string.ToCharArray)
foreach (char c in longString.ToCharArray()) { }

//4 use StringReader
int strLength = longString.Length;
StringReader sr = new StringReader(longString);
for (int i = 0; i < strLength; i++) { c = Convert.ToChar(sr.Read()); }

//5 StreamWriter => StreamReader
int strLength = longString.Length;
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(longString);
writer.Flush();
stream.Position = 0;
StreamReader str = new StreamReader(stream);
while (stream.Position < strLength) { c = Convert.ToChar(str.Read()); }

//6 GetBytes() => StreamReader
int strLength = longString.Length;
MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(longString));
StreamReader str = new StreamReader(stream);
while (stream.Position < strLength) { c = Convert.ToChar(str.Read()); }

//7 GetBytes() => BinaryReader
int strLength = longString.Length;
MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(longString));
BinaryReader br = new BinaryReader(stream, Encoding.Unicode);
while (stream.Position < strLength) { c = br.ReadChar(); }

//8 foreach (c in string)
foreach (char c in longString) { }
Принятый ответ:

Я интерпретировал @CodeInChaos и примечания Бена следующим образом:

Код: Выделить всё

fixed (char* pString = longString) {
char* pChar = pString;
for (int i = 0; i < strLength; i++) {
c = *pChar ;
pChar++;
}
}
Выполнение 100 итераций по короткой строке составило 4,4 мс при стандартном отклонении < 0,1 мс.

Подробнее здесь: https://stackoverflow.com/questions/879 ... -string-in

Вернуться в «C#»