Как я могу реализовать буфер FIFO, к которому я могу эффективно добавлять произвольно размерные куски байтов в голову и из которых я могу эффективно вырезать куски произвольного размера байтов из хвоста?
Фон:
У меня есть класс, который читает байты из файловых объектов в кусках произвольного размера и сам является файлоподобным объектом, из которого клиенты могут читать байты в кусках произвольного размера.
Способ, которым я реализован, заключается в том, что всякий раз, когда клиент хочет прочитать кусок байтов, класс будет многократно читать из базовых файловых объектов (с размерами блоков, соответствующих этим объектам) и добавлять байты в голову очереди FIFO до тех пор, пока в очереди не будет достаточно байтов, чтобы обслуживать кусок запрашиваемого размера клиенту. Затем он выталкивает эти байты из хвоста очереди и возвращает их клиенту.
У меня проблема с производительностью, которая возникает, когда размер блока для основных файловых объектов намного больше размера блока, который клиенты используют при чтении из класса.
Скажем, размер блока для основных файловых объектов - 1 MiB, а размер куска, который клиент читает, равен 1 KiB. В первый раз, когда клиент запрашивает 1 KiB, класс должен прочитать 1 MiB и добавить его в очередь FIFO. Затем для этого запроса и последующих запросов 1023 класс должен вывести 1 KiB из хвоста очереди FIFO, который постепенно уменьшается в размере от 1 MiB до 0 байтов, и в это время цикл начинается снова.
В настоящее время я реализовал это с помощью объекта StringIO. Запись новых байтов в конец объекта StringIO выполняется быстро, но удаление байтов с начала происходит очень медленно, потому что должен быть создан новый объект StringIO, который содержит копию всего предыдущего буфера минус первый кусок байтов.
Вопросы SO, которые касаются похожих вопросов, как правило, указывают на контейнер deque. Тем не менее, deque реализуется как двойной список. Написание фрагмента для дека потребует разделения куска на объекты, каждый из которых содержит один байт. Затем Deque добавит два указателя на каждый объект для хранения, что, вероятно, увеличит требования к памяти, по крайней мере, на порядок по сравнению с байтами. Кроме того, потребуется пройти много времени, чтобы пересечь связанный список и иметь дело с каждым объектом как для разделения кусков на объекты, так и для объединения объектов в куски.