3

我现在正在编写一个生成文件的程序。我想知道 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)可用缓冲区/内存耗尽时,然后将其写入文件。我已将#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],存在不同的缓冲区大小。

4

1 回答 1

4

您正在寻求最佳实践,因此在这方面,我会说最佳实践是将数据转储到流上,而不管特定流是否与文件相关联。在您的情况下,这意味着您不应该在到达磁盘上的真实流之前使用中间流。

现在,鉴于您遇到的性能问题,我的建议是更好地了解它的原因,而不是像您尝试做的那样找到解决方法。

在流的情况下,操作执行不佳的主要原因nextPutAll:是特定消息的特定风格,nextPutAllUnicode:在您的情况下,没有利用特定流类中内置的优化。

更准确地说,大多数流nextPutAll:通过在一个操作中转储数据参数来优化(和朋友)。这通常比语义等效的迭代快得多:

data do: [:token | stream nextPut: token]

这不仅比单个操作优化发送的消息多得多,而且还加剧了 FFI 等所花费的时间。

所以,为了给你一个行动方面的提示,我的建议是调试代码并查看为什么nextPutAllUnicode:没有被优化,并根据这种理解更改你的代码,以便它允许单个操作发生。

于 2019-04-17T15:38:32.733 回答