【发布时间】:2011-08-24 08:08:24
【问题描述】:
不要问我是如何到达那里的,但我正在玩一些掩蔽,循环展开等。无论如何,出于兴趣,我正在考虑如何实现 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)
(不,这不是过早的优化,它不适合我正在搞砸的项目):-)
更新:感谢 Oliver 提供有关 nullcheck 和 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
Update2:这个版本更快(特别是对于长草堆的情况):
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)似乎介于两者之间。不过,我对巨大的差异感到惊讶。
【问题讨论】:
-
mask1 应该是 0xffff 而不是 0xff
标签: c# string performance