【问题标题】:Any faster way to set console colors?任何更快的方法来设置控制台颜色?
【发布时间】:2017-05-18 04:41:36
【问题描述】:

正在分析我的代码,发现我们处理彩色控制台文本的方式非常昂贵(大部分运行时)。

    DateTime dt = DateTime.Now;

    for (int i = 0; i <= 20000; i++)
    {
        ConsoleColor cf = Console.ForegroundColor;
        ConsoleColor cb = Console.BackgroundColor;
        Console.ForegroundColor = ConsoleColor.Red;
        Console.BackgroundColor = ConsoleColor.Blue;
        Console.WriteLine("Hello World");
        Console.ForegroundColor = cf;
        Console.BackgroundColor = cb;
    }

    System.Diagnostics.Debug.WriteLine((DateTime.Now - dt).TotalMilliseconds);

这个简单的循环在我的机器上运行需要 2.8 秒。如果我只做 WriteLine,它只有 600 毫秒。

现在,在我得到巨魔答案之前 :) 问为什么我在硬编码时一直设置颜色,这是测试代码,在真实代码中,前景和背景颜色是根据几个不同的因素计算的。那部分无关紧要。这段代码只是假设颜色会发生变化,因此会保存原件,更改颜色,然后将其改回原件。

自从使用 ILSpy 以来,我还尝试通过 pinvoke 使用本机 SetConsoleTextAttribute 方法,似乎 Console.xxx 方法做了很多额外的废话,但我得到的时间大致相同。

【问题讨论】:

  • 在控制台中更改一个属性(或者实际上用控制台做任何事情)涉及到 P/Invoking 控制台进程。这样做会引入一些重要的开销是有道理的。
  • @Abion47 我曾经有过 pinvoke 比调用 C# 包装器更快的时候,因为它们做了很多事情来更加防弹,而使用直接 pinvoke 调用,您可以绕过所有这些。 C# 包装器可能只有几百行代码:)。似乎在控制台中更改文本颜色应该是微不足道的。
  • 不要使用 DateTime 进行时间计算 - 使用 Stopwatch
  • @DanielA.White 虽然一般来说这是一个很好的建议,它不会对 0.6 秒和 2.8 秒产生太大影响。

标签: c# winapi


【解决方案1】:

请参阅answer 到“如何将快速彩色输出写入控制台?”并注意此解决方案需要 p 调用 WriteConsoleOutput

颜色信息在CharInfo结构体中,在short Attributes字段的低4位使用Console.ForegroundColor,在高4位使用BackgroundColor

这是我用于结构的构造函数:

public CharInfo(char character, ConsoleColor? foreground = null, ConsoleColor? background = null) {
    this.Char = new CharUnion() { UnicodeChar = character };
    this.Attributes = (ushort)((int)(foreground ?? 0) | (((int)(background ?? 0)) << 4));
}

(您还需要在 [StructLayout...] 属性中的几个位置添加 CharSet=CharSet.Unicode 才能使 Unicode 正常工作)

通过为控制台窗口构造一个大小为 CharInfo[] 的数组来做一些缓冲,然后将其一次性刷新到屏幕上以实现极快的更新!

【讨论】:

    【解决方案2】:

    不是真的。正如您已经注意到的,Console 内置的属性直接与SetConsoleTextAttribute 交互,这是在 Windows 中设置控制台输出属性的方法。


    如果 ANSI 颜色是一个选项,这将对您使用控制台颜色的逻辑进行重大更改,它 更快。一个最小的案例

    for (int i = 0; i <= 20000; i++)
    {
        Console.WriteLine("\x1b[31m\x1b[44mHello World\x1b[39m\x1b[49m");
    }
    

    在我的机器上运行大约 1200 毫秒,而使用 Console.xxxColor 属性大约需要 7000 毫秒。

    您需要一个兼容的外壳。 Ansicon 有效。


    使用 ANSI 颜色,您还可以对输出进行批量处理,从而节省更多时间。即使是 20 行的批次,也会将上面的运行时间减半。*如果您出于某种原因向控制台发送垃圾邮件,这可能会有很大帮助。

    *在我的机器上。

    【讨论】:

    • 是的,前几天我也想过用ANSI,但是后来读到标准控制台不支持,所以我什至没有尝试。在今晚开车回家的路上,我考虑编写自己的 set 函数,它可以在一次调用中完成背景和前景(因为 SetConsoleTextAttribute 可以做到这一点),但这只有在用户同时设置两种颜色时才有帮助。可能明天我会尝试编写一个原生 C++ 控制台应用程序,看看 SetConsoleTextAttribute 在那里的表现如何......但 ANSI 速度如此之快的事实表明某处存在无用的开销......
    • 我在原生 C++ 中尝试过,20,000 次迭代只用了 1.24 秒(而在 C# 中为 2.8 秒)。作为比较,仅 C++ 中的 printf 花费了 586 毫秒,这与单独的 C# 输出所花费的时间相当。我从未见过 pinvoke 会增加这么多开销。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-14
    • 2016-03-04
    • 2011-09-04
    • 1970-01-01
    • 2013-06-15
    相关资源
    最近更新 更多