【问题标题】:C# String.PadRight performance vs. concatenating string + substring of pad charactersC# String.PadRight 性能与连接字符串 + 填充字符的子字符串
【发布时间】:2017-12-24 16:54:54
【问题描述】:

不是一个真正的问题,但我没有找到任何答案,所以我自己进行了一些测试,并认为其他人可能会受益。

首先是问题:

当使用预定义字符将(大量)字符串填充到预定义长度时,使用String.PadRight() 或将字符串与预设置的填充字符字符串的子字符串连接是否更快?

【问题讨论】:

  • 不清楚您要填充或连接什么。所以这个问题只能自己回答了。
  • 我很确定您大大低估了正确和有意义的基准测试的难度。 - 您可能还想查看 c# 源代码!
  • @TimSchmelter 好吧,我最初在一篇文章中既有问题又有下面的答案,直到我看到我可以直接将它们分开。但是由于我在发布问题的同时发布了我的答案,所以我假设无论谁没有单独找到足够的问题,也可以从答案中获得更多关于我的目标到底是什么的信息;)
  • @TaW 哦,毫无疑问,我还有很多东西要学。这正是我能够在这方面投入多少时间——我主要是想检查在我的应用程序中以类似的方式使用一种或另一种方式是否会导致巨大的性能损失(或优势) ,这样我就可以首先避免该领域的问题。
  • 您可能(或可能不)喜欢阅读一些these links

标签: c# string performance padding


【解决方案1】:

在找不到答案后我自己进行了测试(这就是首先发布帖子的原因)。

我的结果如下(多次运行类似)

direct String.PadRight average: 29,291921 ms.
String.PadRight average:        32,328434 ms.
Custom PadRight average:        27,066596 ms.

private const int Iterations = 500;
private const int NumStrings = 250000;
private const int TestStringLength = 50;

对于我生成的NumStrings 随机字符串少于TestStringLength 字符的测试,然后用空格将它们全部填充到TestStringLength

所以基于子字符串的填充似乎更快(但当然不那么通用)。有趣的是,填充的字符串越少,运行的迭代越少,这种趋势就会逆转。然后,String.PadRight 变得更快。

测试代码(here a dotnetfiddle version [减少计数以使其在那里工作,你不能相信你得到的结果,它们因运行而异]):

#region Performance Test
private const int Iterations = 500;
private const int NumStrings = 250000;
private const int TestStringLength = 50;

private static string EmptyLine;

public static string PadRight(string input)
{
    return input.PadRight(TestStringLength, ' ');
}
public static string PadRight2(string input)
{
    return input + EmptyLine.Substring(0, TestStringLength - input.Length);
}
#endregion // Performance Test

循环是

#region Performance Test

EmptyLine = String.Join("", Enumerable.Repeat(" ", TestStringLength));

var random = new System.Random();
StringBuilder temp = new StringBuilder();
string[] randomStrings = new string[NumStrings];
double[] averageDirect = new double[Iterations];
double[] averageStandard = new double[Iterations];
double[] averageCustom = new double[Iterations];

// init random strings
for (int i = 0; i < NumStrings; ++i)
{
    temp.Clear();
    for (int k = 0; k < random.Next(0, TestStringLength); ++k)
    {
        temp.Append((char)('!' + random.Next(0, 93)));
    }
    randomStrings[i] = temp.ToString();
}

var timer = new Stopwatch();
string padded;

for (int counter = 0; counter < Iterations; ++counter)
{
    timer.Reset();

    timer.Start();
    for (int i = 0; i < NumStrings; ++i)
    {
        padded = PadRight2(randomStrings[i]);
    }
    timer.Stop();
    averageCustom[counter] = timer.Elapsed.TotalMilliseconds;

    timer.Reset();

    timer.Start();
    for (int i = 0; i < NumStrings; ++i)
    {
        padded = PadRight(randomStrings[i]);
    }
    timer.Stop();
    averageStandard[counter] = timer.Elapsed.TotalMilliseconds;

    timer.Reset();

    timer.Start();
    for (int i = 0; i < NumStrings; ++i)
    {
        padded = randomStrings[i].PadRight(TestStringLength, ' ');
    }
    timer.Stop();
    averageDirect[counter] = timer.Elapsed.TotalMilliseconds;
}

Console.WriteLine($"direct String.PadRight average: {averageDirect.Average()} ms.");
Console.WriteLine($"String.PadRight average: {averageStandard.Average()} ms.");
Console.WriteLine($"Custom PadRight average: {averageCustom.Average()} ms.");

#endregion // Performance Test

【讨论】:

  • 旁注:我可以推荐使用BenchmarkDotNet 进行此类测试。易于使用,结果更准确。请记住运行编译为发行版且没有附加调试器的测试(例如,作为不在 Visual Studio 中盯着的控制台应用程序)。当然,测试本身应该只测试它们应该测试的,而不是不相关的代码(例如,两种方法的相同之处)。
猜你喜欢
  • 2016-08-11
  • 1970-01-01
  • 2015-10-14
  • 1970-01-01
  • 1970-01-01
  • 2016-07-19
  • 1970-01-01
  • 2011-12-06
  • 1970-01-01
相关资源
最近更新 更多