在我当前的项目中,我必须解析一个字符串,并将其中的一部分写入控制台。在测试如何在没有太多开销的情况下执行此操作时,我发现我测试的一种方法实际上比Console.WriteLine更快,这对我来说有点混乱。
我知道这不是 正确的基准测试方法,但我通常很好,粗略的“这比这更快”,我可以告诉它运行一些次。
static void Main(string[] args)
{
var timer = new Stopwatch();
timer.Restart();
Test1("just a little test string.");
timer.Stop();
Console.WriteLine(timer.Elapsed);
timer.Restart();
Test2("just a little test string.");
timer.Stop();
Console.WriteLine(timer.Elapsed);
timer.Restart();
Test3("just a little test string.");
timer.Stop();
Console.WriteLine(timer.Elapsed);
}
static void Test1(string str)
{
Console.WriteLine(str);
}
static void Test2(string str)
{
foreach (var c in str)
Console.Write(c);
Console.Write('\n');
}
static void Test3(string str)
{
using (var stream = new StreamWriter(Console.OpenStandardOutput()))
{
foreach (var c in str)
stream.Write(c);
stream.Write('\n');
}
}
如您所见,Test1正在使用Console.WriteLine。我的第一个想法是简单地为每个字符调用Write,参见Test2。但这导致大约两倍的时间。我的猜测是它会在每次写入后刷新,这会使它变慢。所以我尝试使用StreamWriter(AutoFlush off)测试Test3,导致比Test1快25% ,我真的很好奇为什么会这样。或者是否无法正确地对控制台进行基准测试? (在添加更多测试用例时注意到一些奇怪的数据......)
有人可以启发我吗?
此外,如果有更好的方法(通过字符串并只将部分内容写入控制台),请随意评论。
答案 0 :(得分:5)
首先,我同意其他评论,即你的测试工具留下了一些不可取的东西......我重写了它并将其包含在下面。重写后的结果发布了一个明显的赢家:
//Test 1 = 00:00:03.7066514
//Test 2 = 00:00:24.6765818
//Test 3 = 00:00:00.8609692
从这一点来看,你是正确的,缓冲流编写器的速度要快25%。它的速度更快,因为它是缓冲的。在内部,StreamWriter实现使用大约1~4kb的默认缓冲区大小(取决于流类型)。如果使用8字节缓冲区(允许的最小值)构造StreamWriter,您将看到大部分性能改进消失。您还可以通过在每次写入后使用Flush()调用来查看此内容。
以下是为了获得上述数字而重写的测试:
private static StreamWriter stdout = new StreamWriter(Console.OpenStandardOutput());
static void Main(string[] args)
{
Action<string>[] tests = new Action<string>[] { Test1, Test2, Test3 };
TimeSpan[] timming = new TimeSpan[tests.Length];
// Repeat the entire sequence of tests many times to accumulate the result
for (int i = 0; i < 100; i++)
{
for( int itest =0; itest < tests.Length; itest++)
{
string text = String.Format("just a little test string, test = {0}, iteration = {1}", itest, i);
Action<string> thisTest = tests[itest];
//Clear the console so that each test begins from the same state
Console.Clear();
var timer = Stopwatch.StartNew();
//Repeat the test many times, if this was not using the console
//I would use a much higher number, say 10,000
for (int j = 0; j < 100; j++)
thisTest(text);
timer.Stop();
//Accumulate the result, but ignore the first run
if (i != 0)
timming[itest] += timer.Elapsed;
//Depending on what you are benchmarking you may need to force GC here
}
}
//Now print the results we have collected
Console.Clear();
for (int itest = 0; itest < tests.Length; itest++)
Console.WriteLine("Test {0} = {1}", itest + 1, timming[itest]);
Console.ReadLine();
}
static void Test1(string str)
{
Console.WriteLine(str);
}
static void Test2(string str)
{
foreach (var c in str)
Console.Write(c);
Console.Write('\n');
}
static void Test3(string str)
{
foreach (var c in str)
stdout.Write(c);
stdout.Write('\n');
}
答案 1 :(得分:3)
我已经运行了10000次测试,结果在我的机器上显示如下:
test1 - 0.6164241
test2 - 8.8143273
test3 - 0.9537039
这是我使用的脚本:
static void Main(string[] args)
{
Test1("just a little test string."); // warm up
GC.Collect(); // compact Heap
GC.WaitForPendingFinalizers(); // and wait for the finalizer queue to empty
Stopwatch timer = new Stopwatch();
timer.Start();
for (int i = 0; i < 10000; i++)
{
Test1("just a little test string.");
}
timer.Stop();
Console.WriteLine(timer.Elapsed);
}
答案 2 :(得分:2)
我更改了代码以运行每次测试1000次。
static void Main(string[] args) {
var timer = new Stopwatch();
timer.Restart();
for (int i = 0; i < 1000; i++)
Test1("just a little test string.");
timer.Stop();
TimeSpan elapsed1 = timer.Elapsed;
timer.Restart();
for (int i = 0; i < 1000; i++)
Test2("just a little test string.");
timer.Stop();
TimeSpan elapsed2 = timer.Elapsed;
timer.Restart();
for (int i = 0; i < 1000; i++)
Test3("just a little test string.");
timer.Stop();
TimeSpan elapsed3 = timer.Elapsed;
Console.WriteLine(elapsed1);
Console.WriteLine(elapsed2);
Console.WriteLine(elapsed3);
Console.Read();
}
我的输出:
00:00:05.2172738
00:00:09.3893525
00:00:05.9624869
答案 3 :(得分:2)
我也运行了10000次这样的结果:
00:00:00.6947374
00:00:09.6185047
00:00:00.8006468
这似乎与其他人观察到的一致。我很好奇为什么Test3
比Test1
慢,所以写了第四个测试:
timer.Start();
using (var stream = new StreamWriter(Console.OpenStandardOutput()))
{
for (int i = 0; i < testSize; i++)
{
Test4("just a little test string.", stream);
}
}
timer.Stop();
这个为每个测试重用流,从而避免每次重新创建它的开销。结果:
00:00:00.4090399
虽然这是最快的,但它会在using
块的末尾写入所有输出,这可能不是您所追求的。我想这种方法也会扼杀更多的记忆。