リンクリストベースの実装が遅いと思う理由を知ることは興味深いでしょう. これはキューの古典的な実装です。挿入と削除は、私が想像できるほど高速です (1 つまたは 2 つの参照フィールドを更新します)。あなたが話しているコードを見ていないことに注意してください。
配列を使用してリング バッファーを実装できます。いくつかのこと(すべての要素をトラバースするなど)では高速かもしれませんが、最大サイズがあります(最も簡単な実装の場合)。配列の初期化にも複雑さがあります。
一般的に言えば、データ構造の関数型 (不変) の実装は、対応する命令型 (可変) のバージョンよりも少し遅くなります。優れたパフォーマンスを発揮する非常に優れた関数型キューの実装があります。基本的なトリックは、キューの半分を正順 (高速削除用) に、半分を逆順 (高速挿入用) に保つことです。私が言ったように、逆転のための余分なパスは小さなペナルティをもたらします.
メモリ (割り当てと GC) を見ている場合、不変のアプローチが最も多く割り当て、従来の方法は中程度の量であり、リング バッファーはほとんど割り当てません。しかし、適切な FP 実装 (OCaml など) では、メモリの割り当てが非常に高速になり、オブジェクトの寿命が短い場合でもメモリの再利用が遅くなりません。
編集:ラップトップ(3.06 GHz Intel Core 2 Duo)で非常に大雑把なタイミングテストを実行したところ、標準の OCaml Queue モジュールを使用して 70 ミリ秒で 100 万の値をキューに入れたり取り出したりすることができました。したがって、10,000 を実行するのに約 0.7 ミリ秒かかります (これが正しければ)。あなたの CPU が私のものよりも速くなければ、あなたが望むことをするのに十分な速さではないようです。
編集 2:値をキューに入れるために使用できる既存の参照フィールドが値にあると想定するコードを手書きしました。これにより、キューイングコードでの割り当てが回避されますが、それ以外は標準の Queue モジュールに似ています (ただし、はるかに洗練されていません)。上記と同じラップトップでは、100 万回のキューとデキューに約 48 ミリ秒かかります。なので少し早いです。
編集 3:今では答えが得られている可能性が非常に高いですが、上で概説した純粋なキューの実装を使用して大まかなタイミング テストを実行したところ、100 万回のキューとデキューを実行するのに約 18 ミリ秒かかりました。そのため、かなり高速です。OCamlは純粋な計算用に調整されているため、これはもっともらしいです。これが私のコードです。エラーがないか確認できます。
type q = {
head: int list;
tail: int list;
}
let q_add q i =
{ q with tail = i :: q.tail }
let q_take q =
match q.head with
| [] ->
begin
match List.rev q.tail with
| [] -> raise Not_found
| h :: t -> (h, { head = t; tail = [] })
end
| h :: t -> (h, { head = t; tail = q.tail })
let main () =
let q = q_add { head = []; tail = [] } 0
in let now = Unix.gettimeofday ()
in let rec go q i =
if i > 1_000_000 then
q
else
let (_, q') = q_take (q_add q i)
in
go q' (i + 1)
in let _ = go q 1
in let duration = Unix.gettimeofday () -. now
in
Printf.printf "%f\n" duration
let () = main ()
私が言ったように、これは大雑把なテストです。キューは、長さが 1 要素と 2 要素の間で交互に繰り返されます。結果があなたの環境で保持されるかどうかはわかりません。しかし、それは興味深いです。