Оптимальный размер потока (ReadStream, WriteStream и т. Д.) - PullRequest
3 голосов
/ 16 апреля 2019

Я сейчас пишу программу, которая генерирует файл. Я задавался вопросом, каковы лучшие практики в Стриме (ах), особенно когда речь идет о размере? Я могу себе представить, что если поток становится слишком большим, это может привести к замедлению или другим проблемам с производительностью.

У меня есть следующий код, который можно вызывать много-много раз, так как коллекция может быть огромной. Я предполагаю, что для разных размеров нужно вести себя по-разному, например, <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 Ответ

4 голосов
/ 17 апреля 2019

Вы запрашиваете передовые практики, поэтому в этом отношении я бы сказал, что передовой практикой является создание дампа данных в потоках независимо от того, связан ли конкретный поток с файлом или нет.В вашем случае это означает, что вы не должны использовать промежуточный поток перед тем, как перейти к реальному на диске.

Теперь, учитывая возникшую проблему с производительностью, я рекомендую лучше понять причинуэто в отличие от поиска обходного пути, как вы пытаетесь это сделать.

В случае потоков главная причина плохой работы операции nextPutAll: заключается в том, что особая разновидность конкретного сообщения *В вашем случае 1006 * не использует преимущества оптимизации, встроенной в определенный класс потока.

Точнее, большинство потоков оптимизируют nextPutAll: (и друзей), сбрасывая аргумент данных за одну операцию.Это обычно намного быстрее, чем семантически эквивалентная итерация:

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

, которая не только отправляет намного больше сообщений, чем оптимизация одной операции, но также усугубляет время, затрачиваемое FFI и т. Д.

Итак, чтобы дать вам подсказку о действиях, я бы предложил отладить код и понять, почему nextPutAllUnicode: не оптимизируется, и с этим пониманием изменить ваш код так, чтобы он позволял выполнять одну операцию.

...