OutputBase.Write 会为每个清洁单元发出一个 Cursor 定位调用,使每次重绘都需要执行 rows × cols 次系统调用。

作者: Scripturd创建于 2026年9月7日更新于 2026年9月7日
标签bug

Terminal.Gui/Drivers/Output/OutputBase.cs 中的 Write(IOutputBuffer) (从 2.4.17 反编译而来):

csharp
for (int j = num; j < cols; j++)
{
    lastCol = -1;
    int outputWidth = 0;
    for (; j < cols; j++)
    {
        bool flag3 = IsRasterCoveredBlankCell(buffer, i, j, rasterCellRectangles);
        if (!buffer.Contents[i, j].IsDirty | flag3)          // cell is CLEAN
        {
            if (flag3) { buffer.Contents[i, j].IsDirty = false; }
            if (stringBuilder.Length > 0)
                WriteToConsole(stringBuilder, ref lastCol, ref outputWidth);
            else if (lastCol == -1)
                lastCol = j;
            if (lastCol + 1 < cols) { lastCol++; }
            SetCursorPositionImpl(lastCol, i);               // ← one I/O op per skipped cell
            continue;
        }
        // ...
    }
}

lastCol 每次迭代增加 1,因此位置总是不同,调用总是执行 I/O:

  • ansiAnsiOutput.SetCursorPositionImplWrite(ReadOnlySpan<char>),它分配一个新的 StringBuilder,执行 Encoding.UTF8.GetBytes,并为 ~7 字节的 CSI r;cH 发出自己的 WriteFile
  • dotnetNetOutput.SetCursorPositionImplConsole.SetCursorPosition,位于一个简单的 try {} catch {} 中。它调用 ConsolePal.GetBufferInfo,如果缓冲区无法查询,则 每个单元格都会抛出和吞噬一个 Win32 异常

测量结果

内容来源: tui-cs/Terminal.Gui