none
Console.WriteLine()を含むプログラムの計算時間について RRS feed

  • 質問

  • 以下のC#プログラムを実行しますと,

    for文の1回目の繰返しは計算時間が20秒ほどかかりますが,

    2回目以降の全ての繰返しは1秒もかかりません(何度実行しても同じ結果になります).

    Console.WriteLine()をfor文の中で使用した場合に,

    1回目の繰返しだけが異常に計算時間がかかるのですが,

    このような現象は正常な挙動でしょうか?

    基本的なことかもしれませんが,ご回答のほど宜しくお願い致します.

    [環境]

    Visual Studio Community 2017 Version 15.7.4

    .NET Framework Version 4.7.03056

    Windows 10 Pro 64bit Version 1803

    Visual C# コンソールアプリ(.NET Framework)

    using System;
    using System.Diagnostics;
    
    class SampleProgram
    {
        static void Main()
        {
            Stopwatch swt = new Stopwatch();
            swt.Start();
    
            for (int count = 1; count <= 100; count++)
            {
                double et1 = swt.ElapsedMilliseconds / 1000.0;
    
                for (int i = 1; i <= 10000; i++)
                {
                    Console.WriteLine("{0, 1}", i);
                }
    
                double et2 = swt.ElapsedMilliseconds / 1000.0;
    
                Console.WriteLine("計算時間={0, 5:F3}", et2 - et1);
                Console.ReadLine();
            }
    
            swt.Stop();
        }
    }


    • 編集済み mtforest 2018年7月24日 6:24
    2018年7月24日 6:22

回答

  • 分かった気がします。コマンドプロンプトとのタイトルバーを右クリックして「プロパティ」→「レイアウト」タブの中の「画面バッファのサイズ」の中にある「高さ」の値が影響しているようですね。標準出力は、この値までは遅く、この値を超えると速くなるようです。Windows 10 では、デフォルト 9001 となっていて、Windows 7 では、デフォルト 300 となっていました。確認したところ Windows 7 でも 300 行くらいまでは遅く感じました。

    追伸:

    下記のように SetConsoleScreenBufferSize 関数を使って、画面バッファのサイズを設定することができます。(ただ、バッファサイズを 0 にすることはできないようです。)。Windows 10 の環境でバッファサイズを 300 x 300 にすると 1 回目のループが高速になることが確認できるかと思います。

    using System;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            private const int STD_OUTPUT_HANDLE = -11;
    
            [StructLayout(LayoutKind.Sequential)]
            struct COORD
            {
                public short x;
                public short y;
            }
    
            [DllImport("kernel32.dll", SetLastError = true)]
            static extern IntPtr GetStdHandle(int nStdHandle);
    
            [DllImport("kernel32.dll", SetLastError = true)]
            static extern bool SetConsoleScreenBufferSize(IntPtr hConsoleOutput, COORD dwSize);
    
            static bool SetBufferSize(short x, short y)
            {
                COORD Home;
                Home.x = x;
                Home.y = y;
                return SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), Home);
            }
    
            static void Main(string[] args)
            {
                // 画面バッファのサイズを指定
                SetBufferSize(300, 300);
    
                Stopwatch swt = new Stopwatch();
                swt.Start();
    
                for (int count = 1; count <= 100; count++)
                {
                    double et1 = swt.ElapsedMilliseconds / 1000.0;
    
                    for (int i = 1; i <= 10000; i++)
                    {
                        Console.WriteLine("{0, 1}", i);
                    }
    
                    double et2 = swt.ElapsedMilliseconds / 1000.0;
    
                    Console.WriteLine("計算時間={0, 5:F3}", et2 - et1);
                    Console.ReadLine();
                }
    
                swt.Stop();
            }
        }
    }

    参考サイト: https://msdn.microsoft.com/ja-jp/library/cc429751.aspx

    • 回答としてマーク mtforest 2018年7月24日 13:55
    • 編集済み kenjinoteMVP 2018年7月25日 1:33
    2018年7月24日 7:08

すべての返信

  • 原因や回避方法が分かったわけではないのですが、

    私の環境 (Windows 10 x64 1607) でも再現しました。1 回目のループは 4.328 秒なのに、2 回目のループは 0.554 秒でした。

    いろいろと試したところ、他の言語(Cなど)でも再現し、現象としては標準出力の行数が 10000 行目以降から高速化するようです。Windows 7 で試したところ再現しなかった(1回目のループから高速)ので、Windows 10 のコマンドプロンプト固有の問題なのかなと思いました。

    2018年7月24日 6:52
  • まあ初回実行は重いものですが。

    コンソールのスクロールバーが動くのが重い、っぽいかな?

    スクロールバーのつまみが一番下に到達した後は速くなってる感じ。

    2018年7月24日 7:01
  • 分かった気がします。コマンドプロンプトとのタイトルバーを右クリックして「プロパティ」→「レイアウト」タブの中の「画面バッファのサイズ」の中にある「高さ」の値が影響しているようですね。標準出力は、この値までは遅く、この値を超えると速くなるようです。Windows 10 では、デフォルト 9001 となっていて、Windows 7 では、デフォルト 300 となっていました。確認したところ Windows 7 でも 300 行くらいまでは遅く感じました。

    追伸:

    下記のように SetConsoleScreenBufferSize 関数を使って、画面バッファのサイズを設定することができます。(ただ、バッファサイズを 0 にすることはできないようです。)。Windows 10 の環境でバッファサイズを 300 x 300 にすると 1 回目のループが高速になることが確認できるかと思います。

    using System;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            private const int STD_OUTPUT_HANDLE = -11;
    
            [StructLayout(LayoutKind.Sequential)]
            struct COORD
            {
                public short x;
                public short y;
            }
    
            [DllImport("kernel32.dll", SetLastError = true)]
            static extern IntPtr GetStdHandle(int nStdHandle);
    
            [DllImport("kernel32.dll", SetLastError = true)]
            static extern bool SetConsoleScreenBufferSize(IntPtr hConsoleOutput, COORD dwSize);
    
            static bool SetBufferSize(short x, short y)
            {
                COORD Home;
                Home.x = x;
                Home.y = y;
                return SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), Home);
            }
    
            static void Main(string[] args)
            {
                // 画面バッファのサイズを指定
                SetBufferSize(300, 300);
    
                Stopwatch swt = new Stopwatch();
                swt.Start();
    
                for (int count = 1; count <= 100; count++)
                {
                    double et1 = swt.ElapsedMilliseconds / 1000.0;
    
                    for (int i = 1; i <= 10000; i++)
                    {
                        Console.WriteLine("{0, 1}", i);
                    }
    
                    double et2 = swt.ElapsedMilliseconds / 1000.0;
    
                    Console.WriteLine("計算時間={0, 5:F3}", et2 - et1);
                    Console.ReadLine();
                }
    
                swt.Stop();
            }
        }
    }

    参考サイト: https://msdn.microsoft.com/ja-jp/library/cc429751.aspx

    • 回答としてマーク mtforest 2018年7月24日 13:55
    • 編集済み kenjinoteMVP 2018年7月25日 1:33
    2018年7月24日 7:08
  • プログラムの実行で表示されたコマンドプロンプトに対して,

    「画面バッファのサイズ」の「高さ」を小さく変更することで高速化されました.

    また,SetConsoleScreenBufferSize関数を使用したプログラムで高速化できることも確認しました.

    本当にありがとうございました.

    2018年7月24日 13:55