【发布时间】:2020-07-03 09:28:12
【问题描述】:
我正在尝试删除一种方法来优化我的代码。似乎我没有获得任何性能,而且“优化”的代码更慢!调用方法比在循环中创建变量更快吗?为什么?
为什么下面的代码更快(1.3-1.5 秒)
public void getPureText(string notClearedText)
{
string peeledText = "";
foreach (var symbol in notClearedText)
{
if(isCyrillic(symbol))
peeledText += Char.ToLower(symbol);
else
peeledText += " ";
}
}
private bool isCyrillic(int letterCode)
{
switch (letterCode)
{
case 1028: // Є
case 1108: // є
case 1030: // І
case 1110: // і
case 1031: // Ї
case 1111: // ї
case 1168: // Ґ
case 1169: // ґ
case 32: // " "
case 39: // '
//case 45: // -
return true;
default:
return
1040 <= letterCode && letterCode <= 1103 && // Cyrillic
letterCode != 1066 && // Ъ
letterCode != 1067 && // Ы
letterCode != 1098 // ъ
||
65 <= letterCode && letterCode <= 90
||
97 <= letterCode && letterCode <= 122
;
}
}
比“优化”版本(1.5-1.8 秒)?我错过了什么?
public void getPureText(string notClearedText)
{
string peeledText = "";
foreach (var symbol in notClearedText)
{
int letterCode = symbol;
switch (letterCode)
{
case 1028: // Є
case 1108: // є
case 1030: // І
case 1110: // і
case 1031: // Ї
case 1111: // ї
case 1168: // Ґ
case 1169: // ґ
case 32: // " "
case 39: // ' //case 45: // -
peeledText += Char.ToLower(symbol);
break;
default:
if (
1040 <= letterCode && letterCode <= 1103 && // Cyrillic
letterCode != 1066 && // Ъ
letterCode != 1067 && // Ы
letterCode != 1098 // ъ
||
65 <= letterCode && letterCode <= 90
||
97 <= letterCode && letterCode <= 122
)
peeledText += Char.ToLower(symbol);
else
peeledText += " ";
break;
}
}
}
我已经运行了几十个测试使用
void TestPerformance()
{
Stopwatch sw = new Stopwatch();
sw.Start();
_textRepository.getPureText(RawTextExamples.veryLongText);
sw.Stop();
unitTestFormGuess.show(sw.Elapsed.ToString());
}
附: 如您所见,我从 getPureText() 中删除了一些代码,使其返回 void,然后再次测量时间:结果相同。有问题……
附言 配置:调试。
编辑
对于 peeledText 将类型 string 替换为 StringBuilder。
配置:发布。
字符串大小相同:150 KB。
3 系列测试,每个测试 500 次迭代。
-
使用方法
isCyrillic代码:6.63-6.70毫秒 -
内联:6.80-6.90 毫秒(仍然较慢 o_0)
-
内联但使用正则表达式:6.62-6.70 毫秒
-
使用方法
isCyrillic但使用HashSet而不是switch:7.89-8.32 毫秒。
【问题讨论】:
-
不要在 Debug 中执行基准测试,始终使用 Release。不要在附加调试器的情况下执行此操作,也不要自己执行此操作(使用 Benchmarks.Net 之类的东西)。如果你自己做,总是在开始计时之前多次执行该方法,以便正确地 JIT 方法
-
方法调用几乎没有开销。在方法之间移动代码不会改变性能一点点。这里一个潜在的性能提升是使用
StringBuilder而不是所有那些+=调用。但这也可能不会更快,因为创建类的开销可能超过堆栈上的分配 -
您应该对发布版本而不是调试版本进行基准测试。重复测试时,时间相差多少?另外,
StringBuilder有效果吗?我很久没有跟上 .NET 了,但是循环中的+=一次会非常慢(二次)(如果现在已经优化,可能会有复杂性限制,之后就不可能了——虽然这可能不是这里的区别)。同样,通常两个编译器都能够进行优化,例如内联。 -
我还要构建字符串,然后在整个字符串上调用
ToLower,而不是每个单独的字符,但是你必须再次测试它以查看它是否有任何区别 -
RegEx也是另一种选择...
标签: c# if-statement optimization inline