内联方法以提高性能,但在 C# 中执行速度较慢
Inlining a method to increase performance but it executes slower in C#
我正在尝试删除一种方法来优化我的代码。似乎我没有获得任何性能,而且,“优化”代码更慢!调用方法是否比在循环中创建变量更快?为什么?
为什么下面的代码更快(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());
}
P.S。
如您所见,我从 getPureText() 中删除了一些代码,使其 return 无效,然后再次测量时间:相同的结果。有什么不对...
P.P.S。
配置:调试。
编辑
对于 peeledText
将类型 string
替换为 StringBuilder
。
配置:发布。
字符串的大小相同:150 KB。
3 系列测试,每系列 500 次迭代。
使用方法isCyrillic
代码:6.63-6.70毫秒
内联:6.80-6.90 毫秒(仍然较慢 o_0)
内联但使用 RegEx:6.62-6.70 毫秒
使用方法 isCyrillic
但使用 HashSet
而不是 switch
:7.89-8.32 毫秒。
如果代码 运行 处理一个输入字符串需要 1.6 秒,那么它是一个相当大的字符串。我会 stop using string concatenation (+=
) and start using a System.Text.StringBuilder
,它可能更快,内存效率更高:
public void getPureText(string notClearedText)
{
var peeledText = new StringBuilder(notClearedText.Length);
foreach (var symbol in notClearedText)
{
if(isCyrillic(symbol))
peeledText.Append(Char.ToLower(symbol));
else
peeledText.Append(' ');
}
}
如果您在一个有点紧凑的循环中调用 getPureText
,您可以考虑重用 buffer
简单地清除它并避免每次调用的新成本。
在 发布模式下再次对其进行基准测试,放弃预热 运行 并且不附加调试器 。如果仍然不能满足你的性能目标,那么开始微优化内联调用等。抖动是非常聪明的优化代码,所以它可能不会给你带来太多。
long Benchmark(string veryLongText, int repetitions)
{
getPureText(veryLongText); //warmup
var watch = Stopwatch.StartNew();
for (var i = 0; i < repetitions; i++)
getPureText(veryLongText);
watch.Stop();
return watch.ElapsedMilliseconds/repetitions;
}
我正在尝试删除一种方法来优化我的代码。似乎我没有获得任何性能,而且,“优化”代码更慢!调用方法是否比在循环中创建变量更快?为什么?
为什么下面的代码更快(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());
}
P.S。 如您所见,我从 getPureText() 中删除了一些代码,使其 return 无效,然后再次测量时间:相同的结果。有什么不对...
P.P.S。 配置:调试。
编辑
对于 peeledText
将类型 string
替换为 StringBuilder
。
配置:发布。
字符串的大小相同:150 KB。
3 系列测试,每系列 500 次迭代。
使用方法
isCyrillic
代码:6.63-6.70毫秒内联:6.80-6.90 毫秒(仍然较慢 o_0)
内联但使用 RegEx:6.62-6.70 毫秒
使用方法
isCyrillic
但使用HashSet
而不是switch
:7.89-8.32 毫秒。
如果代码 运行 处理一个输入字符串需要 1.6 秒,那么它是一个相当大的字符串。我会 stop using string concatenation (+=
) and start using a System.Text.StringBuilder
,它可能更快,内存效率更高:
public void getPureText(string notClearedText)
{
var peeledText = new StringBuilder(notClearedText.Length);
foreach (var symbol in notClearedText)
{
if(isCyrillic(symbol))
peeledText.Append(Char.ToLower(symbol));
else
peeledText.Append(' ');
}
}
如果您在一个有点紧凑的循环中调用 getPureText
,您可以考虑重用 buffer
简单地清除它并避免每次调用的新成本。
在 发布模式下再次对其进行基准测试,放弃预热 运行 并且不附加调试器 。如果仍然不能满足你的性能目标,那么开始微优化内联调用等。抖动是非常聪明的优化代码,所以它可能不会给你带来太多。
long Benchmark(string veryLongText, int repetitions)
{
getPureText(veryLongText); //warmup
var watch = Stopwatch.StartNew();
for (var i = 0; i < repetitions; i++)
getPureText(veryLongText);
watch.Stop();
return watch.ElapsedMilliseconds/repetitions;
}