最佳流大小(ReadStream、WriteStream 等)
Optimal stream size (ReadStream, WriteStream, etc.)
我正在编写一个生成文件的程序。我想知道关于 Stream(s) 的最佳实践是什么,尤其是在大小方面?我可以想象,如果一个流变得太大,它会带来一些减速或其他性能问题。
我有下面的代码,可以调用很多次,而且集合很大。我认为对于不同大小的行为应该有所不同,例如 <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 的回答和您的建议后的第三个版本(我查看了缓冲区 - 大小定义为 __stringSize(aCollection)
当可用 buffer/memory 耗尽时,然后将其写入文件。我已经删除#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:
在您的情况下,没有利用内置于特定流的优化 class.
更准确地说,大多数流通过在一次操作中转储数据参数来优化 nextPutAll:
(和朋友)。这通常比语义等效的迭代快得多:
data do: [:token | stream nextPut: token]
这不仅发送的消息比单个操作优化多得多,而且还加剧了 FFI 等所花费的时间
因此,为了给您一些行动方面的提示,我的建议是调试代码并查看 nextPutAllUnicode:
未被优化的原因,并根据这一理解更改您的代码,以便它会允许单个操作发生。
我正在编写一个生成文件的程序。我想知道关于 Stream(s) 的最佳实践是什么,尤其是在大小方面?我可以想象,如果一个流变得太大,它会带来一些减速或其他性能问题。
我有下面的代码,可以调用很多次,而且集合很大。我认为对于不同大小的行为应该有所不同,例如 <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 的回答和您的建议后的第三个版本(我查看了缓冲区 - 大小定义为 __stringSize(aCollection)
当可用 buffer/memory 耗尽时,然后将其写入文件。我已经删除#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:
在您的情况下,没有利用内置于特定流的优化 class.
更准确地说,大多数流通过在一次操作中转储数据参数来优化 nextPutAll:
(和朋友)。这通常比语义等效的迭代快得多:
data do: [:token | stream nextPut: token]
这不仅发送的消息比单个操作优化多得多,而且还加剧了 FFI 等所花费的时间
因此,为了给您一些行动方面的提示,我的建议是调试代码并查看 nextPutAllUnicode:
未被优化的原因,并根据这一理解更改您的代码,以便它会允许单个操作发生。