在我当前的项目中,我必须解析一个字符串,并将其中的一部分写入控制台.在测试如何在没有太多开销的情况下执行此操作时,我发现我测试的一种方法实际上比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%,我真的很好奇为什么会这样.或者是否无法正确地对控制台进行基准测试? (在添加更多测试用例时注意到一些奇怪的数据…)
有人可以开导我吗?
解决方法
首先,我同意其他评论,你的测试工具留下了一些需要的东西……我重写了它,并将其包含在下面.重写后的结果发布了一个明显的赢家:
//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'); }