Wie implementiere ich einen FIFO-Puffer, dem ich beliebig große Brocken von Bytes zum Kopf hinzufügen kann und von dem ich beliebig große Stücke von Bytes aus dem Schwanz effizient platzen kann ?Effiziente FIFO-Warteschlange für beliebig große Byte-Stücke in Python
Hintergrund:
Ich habe eine Klasse, die Bytes aus dateiähnlichen Objekten in Stücke beliebiger Größe liest und ist selbst eine dateiähnliche Objekt, von dem die Kunden die Bytes in Stücke beliebiger Größe lesen kann. Die Art, wie ich dies implementiert habe, ist, dass, wann immer ein Client einen Teil der Bytes lesen will, die Klasse wiederholt von den zugrunde liegenden dateiähnlichen Objekten liest (mit Chunk-Größen, die für diese Objekte geeignet sind) und die Bytes zu den Bytes hinzufügt Kopf einer FIFO-Warteschlange, bis genügend Bytes in der Warteschlange vorhanden sind, um dem Client einen Teil der angeforderten Größe zu liefern. Dann werden diese Bytes vom Ende der Warteschlange entfernt und an den Client zurückgegeben.
Ich habe ein Leistungsproblem, das auftritt, wenn die Chunk-Größe für die zugrundeliegenden dateiähnlichen Objekte viel größer ist als die Chunk-Größe, die Clients beim Lesen aus der Klasse verwenden.
Angenommen, die Chunk-Größe für die zugrunde liegenden dateiähnlichen Objekte beträgt 1 MiB, und die Chunk-Größe, mit der der Client liest, beträgt 1 KiB. Wenn der Client zum ersten Mal 1 KiB anfordert, muss die Klasse 1 MiB lesen und zur FIFO-Warteschlange hinzufügen. Dann muss die Klasse für diese Anfrage und die nachfolgenden 1023 Anfragen 1 KiB aus dem Ende der FIFO-Warteschlange holen, die allmählich in der Größe von 1 MiB auf 0 Bytes abnimmt, zu welcher Zeit der Zyklus erneut beginnt.
Ich habe dies derzeit mit einem StringIO-Objekt implementiert. Das Schreiben neuer Bytes am Ende des StringIO-Objekts ist schnell, aber das Entfernen von Bytes vom Anfang an ist sehr langsam, da ein neues StringIO-Objekt erstellt werden muss, das eine Kopie des gesamten vorherigen Puffers minus dem ersten Byte enthält.
SO Fragen, die mit ähnlichen Problemen beschäftigen, neigen dazu, auf den Container zu verweisen. Deque wird jedoch als doppelt verknüpfte Liste implementiert. Das Schreiben eines Chunks in die Deque würde das Teilen des Chunks in Objekte erfordern, die jeweils ein einzelnes Byte enthalten. Die Deque würde dann zwei Zeiger zu jedem Objekt zum Speichern hinzufügen, was wahrscheinlich die Speicheranforderungen um mindestens eine Größenordnung im Vergleich zu den Bytes erhöht. Außerdem würde es lange dauern, die verknüpfte Liste zu durchlaufen und jedes Objekt zu behandeln, um Stücke in Objekte aufzuteilen und Objekte in Stücke zu verbinden.
Ooh, +1 für den Wraparound. Daran hatte ich nicht gedacht. Sie müssen jedoch die maximale Größe im Voraus wissen; tatsächlich, ich nehme an, es könnte nach Bedarf angebaut werden ... – Cameron
Danke! Das sieht perfekt aus. Ich habe ein Experiment mit StringIO durchgeführt, das anzeigt, dass es sich automatisch ausdehnt, um dies zu berücksichtigen. Wenn beispielsweise die aktuelle Größe des StringIO-Objekts 10 Byte und PUTPT (der Suchort) den Index 5 aufweist, wird beim Schreiben eines 20-Byte-Chunks das StringIO-Objekt automatisch auf 25 Byte erweitert, wobei die ersten 5 Byte beibehalten und der Rest überschrieben wird. Wenn GETPT jedoch nach PUTPT ist, ist etwas mehr Logik erforderlich. –
Ich habe diese Idee in meiner Antwort unten implementiert. Prost! – Cameron