Оптимальный размер потока (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: не оптимизируется, и с этим пониманием измените ваш код так, чтобы он позволял выполнять одну операцию.

Другие вопросы по тегам