我正在撰写一个相当大的文本文件(实际上更像是ASCII编码的数据),它非常缓慢,而且使用了很多内存。
这是我正在使用的代码的最简版本,用于测试如何更快地写入文件。writeFileIncrementally
在for循环中一次写入一行,而writeFileFromBigData
创建一个大字符串,然后将其转储到磁盘上。我完全预计writeFileFromBigData
会更快,但它快了20倍! 这比我预期的要多一些。对于size = 10_000_000
,逐行写入需要20-25秒,而一次性写入只需要1-1.5秒。此外,逐步版本会随着进程的进行不断分配更多的内存。到最后,它已经达到GiB级别。我不明白这里发生了什么。
func writeFileIncrementally(toUrl url: URL, size: Int) {
// ensure file exists and is empty
try? "".write(to: url, atomically: true, encoding: .ascii)
guard let handle = try? FileHandle(forWritingTo: url) else {return}
defer {
handle.closeFile()
}
for i in 0..<size {
let s = "\(i)\n"
handle.write(s.data(using: .ascii)!)
}
}
func writeFileFromBigData(toUrl url: URL, size: Int) {
let s = (0..<size).map{String($0)}.joined(separator: "\n")
try? s.write(to: url, atomically: true, encoding: .ascii)
}
与Python相比,创建字符串并将其写入的速度也更快。这是合理的,但在Python中的区别是,逐步写入需要大约2.7秒(约98%的用户时间),而一次性写入(包括创建字符串)只需要约1秒钟。此外,逐步写入版本具有恒定的内存使用量。随着文件的编写,内存不会增加。
def writeFileIncrementally(path, size):
with open(path, "w+") as f:
for i in range(size):
f.write(f"{i}\n")
def writeFileFromBigData(path, size):
with open(path, "w+") as f:
f.write("\n".join(str(i) for i in range(size)))
所以我的问题有两个:
- 为什么我的
writeFileIncrementally
函数会这么慢,而且为什么会使用那么多内存?我希望能够通过逐步写入来减少内存使用。 - 在Swift中,有没有更好的逐步写入大型文本文件的方法?
writeStringsWithBuffer(strings: stuff.lazy.map{foo($0)}, to: url)
。通过使用一个接受字符串序列的函数,我可以给它一个惰性映射来将数据转换为字符串。它在0.7秒内写入了虚拟数据(前一千万个自然数),并且在使用时相当易读。再次感谢! - Simon LundbergFileHandle.write
可能会,但实际上并没有),但是你越少接触Cocoa桥接类型,就越不可能出现这种情况。 - Rob Napier