File.ReadAllText在循环中变慢因每次新建缓冲区、重复开闭句柄及路径解析;应改用ReadAllBytes+UTF8.GetString、缓存DirectoryInfo、用LastWriteTimeUtc.Ticks+Length作缓存键。

为什么 File.ReadAllText 在循环里变慢得离谱
因为每次调用都新建缓冲区、重复打开/关闭句柄、触发完整路径解析和安全检查。JIT 不会帮你把多次读取合并成一次——它只优化单个方法内部的指令流,不跨调用边界做语义合并。
- 高频小文件读取(如配置项轮询)改用
File.ReadAllBytes+Encoding.UTF8.GetString,避免ReadAllText内部的额外字符串处理开销 - 连续读多个同目录文件时,手动缓存
DirectoryInfo实例,别让每次File.Exists都重新解析父路径 - 若文件内容不变,加一层内存字典缓存,键用
FileInfo.LastWriteTimeUtc.Ticks+Length组合,比只用路径更防误击
Span<byte></byte> 能不能真提升文件读写性能
能,但只在你控制整个数据流时生效。JIT 对 Span 的优化前提是:不逃逸到堆、长度编译期可知或运行期稳定、没被装箱。一旦进 List<byte></byte> 或传给旧 API(比如 Stream.Write 的重载不接受 Span),优势就归零。
- 读大文件优先用
FileStream.ReadAsync(Span<byte>, CancellationToken)</byte>,配合栈上分配的stackalloc byte[8192],避免 GC 压力 - 写文件时别用
Span拼接字符串再转byte[]——这反而触发额外拷贝;直接用Utf8Formatter.TryFormat写入预分配的Span<byte></byte> - .NET 6+ 中
MemoryMappedFile创建视图返回MemoryManager<byte></byte>,此时 JIT 可将部分边界检查优化掉,但需确保映射长度不为零且未被 GC 移动
JIT 不会为你内联 FileStream 构造函数的原因
因为 FileStream 构造函数有副作用:它实际调用系统 API 打开句柄、设置缓冲策略、检查权限。JIT 只内联无副作用、成本低于阈值(默认约 32 IL 指令)且标记为 [MethodImpl(MethodImplOptions.AggressiveInlining)] 的方法——而 FileStream 的构造函数既没加这个标记,又远超阈值。
- 不要在 hot path 里反复 new
FileStream,哪怕用了using;改用对象池(FileStreamPool)或复用已打开的实例(注意线程安全) - 如果只是顺序读,
File.OpenRead(path)返回的FileStream默认缓冲区是 4KB,但 JIT 不会根据后续读取模式动态调大它——你得显式传bufferSize: 65536 - 启用
FileOptions.Asynchronous后,JIT 仍按同步路径编译构造函数,异步优化发生在ReadAsync调用时,不是构造时
Release 模式下 File.Copy 还是慢?检查这三个地方
即使开了优化,File.Copy 仍是纯托管实现,不做内存映射、不跳过 ACL 复制、不利用 OS 的 copy-on-write 机制。它的“快”仅限于比手动循环 Read/Write 少几层托管调用,不是本质加速。
- 目标盘是 NTFS 且源目标在同一卷?直接调用 Win32
CopyFileEx并传COPY_FILE_COPY_SYMLINK标志,能触发底层硬链接优化 - 大文件复制卡在 CPU 占用低、IO 等待高?说明瓶颈在磁盘调度,此时加
FileOptions.WriteThrough | FileOptions.NoBuffering反而更慢——JIT 无法绕过内核缓冲区策略 - 路径含中文或特殊字符时,
File.Copy会多一次Path.GetFullPath调用,而 JIT 不会把这步优化掉;提前缓存绝对路径字符串可省掉这部分










