CodeGym /课程 /C# SELF /I/O 输入输出 性能 问题 ( I/O)...

I/O 输入输出 性能 问题 ( I/O)

C# SELF
第 41 级 , 课程 0
可用

1. 为什么输入输出这么慢?

输入输出(I/O)“慢”是个老问题。我们的代码经常比“思考”更多时间在“等”数据。来看看为什么去“仓库”比在“厨房”工作慢。

硬件的物理限制 (Hardware Limitations)

机械硬盘 (HDD)。 有机械结构:盘片旋转,读写头移动。寻道时间和旋转延迟需要时间 — 这导致高延迟。

固态硬盘 (SSD)。HDD 快,没有机械部件,但写入和磨损管理让操作也不是瞬时的。

网络。 取决于带宽和延迟、路由器等。即使是千兆链路,访问远端服务器的响应也是毫秒级,而不是像 CPU 那样的纳秒级。

操作系统开销 (OS Overhead)

  • 权限检查。 进程能否读/写这个文件?
  • 定位文件数据。 文件系统要找到碎片化的块。
  • 缓冲和缓存。 OS 管理缓冲区以提高效率。
  • 上下文切换。 当进程等待 I/O,CPU 会切换 — 这也要时间。

天壤之别:CPU 速度 vs I/O 速度

  • CPU 操作: 0.20.5 纳秒
  • 从 RAM 读取: 10100 纳秒
  • 从 SSD 读取: 50100 微秒
  • 从 HDD 读取: 510 毫秒
  • 网络请求: 10100 毫秒及以上

差距巨大。如果每个字符都“叫快递”来(I/O),你会打印得很慢,不管你的“打字手”(CPU)多快。更有效的做法是按块取数据——句子或段落级别。

2. 在文件内部:实际发生了什么?

操作文件时的命令链看起来像这样:

flowchart TD
    A[你的 C# 代码] --> B[.NET FileStream]
    B --> C[操作系统 Windows / Linux / Mac]
    C --> D[文件系统: NTFS, ext4, APFS]
    D --> E[设备驱动]
    E --> F[物理盘: HDD / SSD]
  • 你的代码 调用,比如 File.ReadAllText(path)
  • .NET 在底层使用 FileStream、缓冲区和系统调用。
  • OS 管理缓存和队列。
  • 文件系统 找到文件的数据块。
  • 驱动 与设备通信。
  • 存储设备 执行物理操作。

每一层都会增加开销。瓶颈通常在物理介质。

3. 示例:实践中慢的代码

反模式:通过 ReadByte() 每次读取一个字节。


// ❌ 通过字节低效读取文件
using FileStream fs = new FileStream("bigfile.txt", FileMode.Open);
int currentByte;
while ((currentByte = fs.ReadByte()) != -1)
{
    // 对字节做点什么
}

为什么这不好?每次调用 ReadByte() 都是一次流访问。大文件上这种调用可能有数百万次,系统把时间花在开销上,而不是做真正的工作。

正确的做法是按块读取:


// ✅ 用大块高效读取文件
byte[] buffer = new byte[4096]; // 4 KB — 标准缓冲区大小
int bytesRead;
using FileStream fs = new FileStream("bigfile.txt", FileMode.Open);
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
{
    // 处理接收到的数据块
}

按大块读取能让 OS 和磁盘更有效地利用缓存和队列 — 执行时间会减少好几倍。

4. 对真实应用的影响

用户界面 (UI)。 阻塞的 I/O 会“卡住”窗口。把操作放到后台/异步,不要阻塞主线程很重要。

Web 服务器和数据库。 服务器不断做读写;慢磁盘或网络会拖慢整个服务。缓冲、连接池和异步 I/O 是提高吞吐的关键。

大数据 (Big Data)。 在处理 GB/TB 级别时,任何低效都会被放大。块大小、顺序访问和流式处理决定成败。

游戏。 长时间加载关卡/资源就是 I/O 问题。正确打包资源并按大块读取能缩短加载时间。

5. 初学者常见错误

常见错误是逐行或逐字节读大文件,使用 ReadByte 或者缓冲太小(例如 256 字节)。系统调用次数增多,性能下降。

也有相反的极端:尝试一次性读入巨大的文件,用 File.ReadAllBytes 然后得到 OutOfMemoryException。更好的折中是选择“合理的块大小”(通常 48 KB 及以上,取决于负载)并使用流式处理。

2
任务
C# SELF, 第 41 级, 课程 0
已锁定
按行读取文件内容
按行读取文件内容
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION