Оптимальный размер потока (ReadStream, WriteStream и т. Д.)
Я сейчас пишу программу, которая генерирует файл. Я задавался вопросом, каковы лучшие практики в Стриме (ах), особенно когда речь идет о размере? Я могу себе представить, что если поток становится слишком большим, это может привести к замедлению или другим проблемам с производительностью.
У меня есть следующий код, который можно вызывать много-много раз, так как коллекция может быть огромной. Я предполагаю, что для разных размеров нужно вести себя по-разному, например, <1 МБ <=> 10 МБ <=> 100 МБ <=> до 1-10 ГБ <=> >10 ГБ
writeIntoStream: anInputStringCollection
aWriteStream := WriteStream on: '' asUnicode16String.
anInputStringCollection do: [ :string |
aWriteStream nextPutAllUnicode: string asUnicode16String.
].
^ aWriteStream
Каковы лучшие практики? Например, нужно ли заботиться, подходит ли он куче или стеку?
На данный момент я пришел к выводу, что если я использую максимум 5 КБ для потока (или коллекции), он достаточно быстрый и работает (для 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
]
Третья версия после ответа Леандро и вам совет (я посмотрел на буфер - размер определяется как __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:
- для окон: bufferSize := 1 * 1024
или * nix bufferSize := 8 * 1024
[КБ].
1 ответ
Вы запрашиваете лучшие практики, поэтому в этом отношении я бы сказал, что лучше всего делать дамп данных в потоках независимо от того, связан ли конкретный поток с файлом или нет. В вашем случае это означает, что вы не должны использовать промежуточный поток перед тем, как перейти к реальному на диске.
Теперь, учитывая проблему с производительностью, с которой я столкнулся, я бы рекомендовал лучше понять ее причину, а не находить обходной путь, как вы пытаетесь это сделать.
В случае потоков основной причиной nextPutAll:
Операция, которую нужно выполнить плохо, заключается в том, что конкретный вид конкретного сообщения, nextPutAllUnicode:
в вашем случае не используется оптимизация, встроенная в конкретный класс потока.
Точнее, большинство потоков оптимизируют nextPutAll:
(и друзья) путем сброса аргумента данных в одной операции. Это обычно намного быстрее, чем семантически эквивалентная итерация:
data do: [:token | stream nextPut: token]
который не только отправляет гораздо больше сообщений, чем оптимизация одной операции, но также увеличивает время, затрачиваемое FFI и т. д.
Итак, чтобы дать вам подсказку о действиях, я бы предложил отладить код и понять, почему nextPutAllUnicode:
не оптимизируется, и с этим пониманием измените ваш код так, чтобы он позволял выполнять одну операцию.