Не спрашивайте, как я туда попал, но я играл с некоторыми маскировками, развертыванием циклов и т. Д. В любом случае, из интереса я думал о том, как реализовать метод indexof, и вкратце, все кроме маскировки и пр., эта наивная реализация:
public static unsafe int IndexOf16(string s, int startIndex, char c) {
if (startIndex < 0 || startIndex >= s.Length) throw new ArgumentOutOfRangeException("startIndex");
fixed (char* cs = s) {
for (int i = startIndex; i < s.Length; i++) {
if ((cs[i]) == c) return i;
}
return -1;
}
}
быстрее, чем string.IndexOf (char). Я написал несколько простых тестов, и, похоже, они точно соответствуют результатам.
Некоторые примеры выходных чисел с моей машины (конечно, они меняются в некоторой степени, но тенденция ясна):
short haystack 500k runs
1741 ms for IndexOf16
2737 ms for IndexOf32
2963 ms for IndexOf64
2337 ms for string.IndexOf <-- buildin
longer haystack:
2888 ms for IndexOf16
3028 ms for IndexOf32
2816 ms for IndexOf64
3353 ms for string.IndexOf <-- buildin
IndexOfChar помечен как внешний, поэтому вы не можете отразить его. Однако я думаю, что это должна быть (нативная) реализация:
http://www.koders.com/cpp/fidAB4768BA4DF45482A7A2AA6F39DE9C272B25B8FE.aspx?s=IndexOfChar#L1000
Кажется, они используют одну и ту же наивную реализацию.
Вопросы приходят мне в голову:
1) Я что-то упустил в своей реализации, которая объясняет, почему это быстрее? Я могу думать только о поддержке расширенных символов, но их реализация предполагает, что они тоже ничего особенного для этого не делают.
2) Я предполагал, что большая часть низкоуровневых методов в конечном итоге будет реализована в ручном ассемблере, что, похоже, не так. Если это так, зачем вообще его реализовывать, а не только в C #, как в моем примере реализации?
(Завершите тестирование здесь (я думаю, что здесь слишком долго вставлять): http://paste2.org/p/1606018)
(Нет, это не преждевременная оптимизация, это не для проекта, о котором я просто балуюсь): -)
Обновление : Спасибо Оливеру за подсказку о нулевой проверке и параметре Count. Я добавил их в свой IndexOf16Implementation так:
public static unsafe int IndexOf16(string s, int startIndex, char c, int count = -1) {
if (s == null) throw new ArgumentNullException("s");
if (startIndex < 0 || startIndex >= s.Length) throw new ArgumentOutOfRangeException("startIndex");
if (count == -1) count = s.Length - startIndex;
if (count < 0 || count > s.Length - startIndex) throw new ArgumentOutOfRangeException("count");
int endIndex = startIndex + count;
fixed (char* cs = s) {
for (int i = startIndex; i < endIndex; i++) {
if ((cs[i]) == c) return i;
}
return -1;
}
}
Числа изменились незначительно, однако это все еще довольно значительно быстрее (результаты 32/64 опущены):
short haystack 500k runs
1908 ms for IndexOf16
2361 ms for string.IndexOf
longer haystack:
3061 ms for IndexOf16
3391 ms for string.IndexOf
Обновление2 : эта версия еще быстрее (особенно для случая с длинным стогом сена):
public static unsafe int IndexOf16(string s, int startIndex, char c, int count = -1) {
if (s == null) throw new ArgumentNullException("s");
if (startIndex < 0 || startIndex >= s.Length) throw new ArgumentOutOfRangeException("startIndex");
if (count == -1) count = s.Length - startIndex;
if (count < 0 || count > s.Length - startIndex) throw new ArgumentOutOfRangeException("count");
int endIndex = startIndex + count;
fixed (char* cs = s) {
char* cp = cs + startIndex;
for (int i = startIndex; i <= endIndex; i++, cp++) {
if (*cp == c) return i;
}
return -1;
}
}
Обновление 4:
Основываясь на обсуждении с LastCoder, я считаю, что это зависит от архитектуры. Мой Xeon W3550 на работе, кажется, предпочитает эту версию, в то время как его i7, похоже, нравится встроенная версия. Моя домашняя машина (Athlon II) кажется промежуточной. Я удивлен такой большой разницей.