我现在正在编写一个生成文件的程序。我想知道Stream上的最佳实践是什么,特别是在尺寸方面?我可以想象,如果流变得太大,它可能会带来一些减速或其他性能问题。
我有以下代码,可以多次调用,但这个集合可能很大。我认为对于不同的大小应该有不同的行为,例如<1MB <=> 10MB <=> 100MB <=>到1-10GB <=>> 10GB
writeIntoStream: anInputStringCollection
aWriteStream := WriteStream on: '' asUnicode16String.
anInputStringCollection do: [ :string |
aWriteStream nextPutAllUnicode: string asUnicode16String.
].
^ aWriteStream
什么是最佳做法?例如,是否应该关心它是适合堆还是堆栈?
现在我已经得出结论,如果我为流(或集合)使用最大5kB,它足够快并且它可以工作(对于Smalltalk / X)。
我想知道不同Smalltalk口味的限制和内部。 (我没有进行任何测试,也找不到任何关于它的文章)
编辑:首先,谢谢大家(@LeandroCaniglia,@ JayK,@ aka.nice)。第一个版本是 - 减速是由许多操作引起的:打开,写入,关闭。逐行写:
write: newString to: aFile
"Writes keyName, keyValue to a file"
"/ aFile is UTF16-LE (Little Endian) Without Signature (BOM)
aFile appendingFileDo: [ :stream |
stream nextPutAllUtf16Bytes: newString MSB: false
]
第二个版本,速度更快,但仍然不正确。有一个中间流,用块写成:
write: aWriteStream to: aFile
"Writes everything written to the stream"
"/ aFile is UTF16-LE Without Signature
aFile appendingFileDo: [ :stream | "/ withoutTrailingSeparators must be there as Stream puts spaces at the end
stream nextPutAllUtf16Bytes: (aWriteStream contents withoutTrailingSeparators) MSB: false
]
第二个版本是在Leandro的anwer和你的建议之后(我查看了缓冲区 - 当可用的缓冲区/内存耗尽时,大小定义为__stringSize(aCollection)
,然后将其写入文件。我已经将#write:to:
一起删除,现在流定义为:
anAppendFileStream := aFile appendingWriteStream.
现在,在流中播放的每个方法都使用:
anAppendFileStream nextPutUtf16Bytes: aCharacter MSB: false.
要么
anAppendFileStream nextPutAllUtf16Bytes: string MSB: false
存在缓冲区大小逻辑,其中缓冲区长度的猜测发生在例如#nextPutAll:
-bufLen = (sepLen == 1) ? len : (len + ((len/4) + 1) * sepLen);)
,其中sepLen
是基于分隔符大小(EOF,cr,crlf)定义的。
对于不同的方法存在不同的缓冲区大小,例如#copyToEndFrom:
- 用于windows:bufferSize := 1 * 1024
或* nix bufferSize := 8 * 1024
[kB]。
您要求最佳实践,因此在这方面我会说最好的做法是将数据转储到流上,而不管特定流是否与文件相关联。在您的情况下,这意味着您在访问磁盘上的实际流之前不应该使用中间流。
现在,考虑到您遇到的性能问题,我的建议是更好地了解它的原因,而不是像您尝试那样找到解决方法。
在流的情况下,nextPutAll:
操作执行效果不佳的主要原因是特定消息的特殊风格(在您的情况下为nextPutAllUnicode:
)没有利用特定流类中内置的优化。
更确切地说,大多数流通过在一个操作中转储数据参数来优化nextPutAll:
(和朋友)。这通常比语义上等效的迭代快得多:
data do: [:token | stream nextPut: token]
它不仅比单一操作优化发送更多的消息,还会加剧FFI等所用的时间。
因此,为了给你一个行动提示,我的建议是调试代码,看看为什么nextPutAllUnicode:
没有被优化,并且通过这种理解改变你的代码,以便它允许单个操作发生。