【Bun】readdir が Node.js より 40 倍高速ってホント?

今日(2023/12/15)現在、Bunのバージョンは v1.0.18まで行ってますが(速いっ)、遅ればせながら2023/12/2 にリリースされた Bun v1.0.15　を眺めていたら、
>「recursiveはfs.readdir()Node.js より 40 倍高速です」

と書いてあったので確かめてみました。

https://bun.sh/blog/bun-v1.0.15#recursive-in-fs-readdir-is-40x-faster-than-node-js


recursive (リカーシブ) は「再帰的」という意味なので、まぁそういう機能ですね。下のディレクトリまで順にみに行ってくれます。

Bunでは、v1.0.15 からですが、Node.js ではリファレンスによると v20.1.0, v18.17.0 と2つ書かれています。(Node.js の方は見ていなかったので詳細は判らないです。)


まずディレクトリをサーチするので、その前に mydir ディレクトリを作り、その下にdir1からdir100までのサブディレクトリを用意して、それをサーチする速度を比較することにします。

まず、mydir1/mydir2/ディレクトリを作りそこへ更にディレクトリを100個シェルで作ります。

```sh:ディレクトリを100個用意します
mkdir mydir1
cd mydir1
mkdir mydir2
cd mydir2

for i in {1..100}; do
  mkdir "dir$i"
done
```
mydir1 の下はこんな感じになります
```sh:できたディレクトリ
$ tree
.
└─ mydir1/
    └─ mydir2/
        ├─ dir1/
        ├─ dir2/
        ├─ dir3/
      省略
        └─ dir100/
```

では、コードをbun用とnode用で2つ作ります。普通は1つで良いのですが、今回は、ES Moduleモードを使ってなるべく同じ感じのコードで比較したいという事で、Node.jsでも ES Moduleが動く 拡張子 .mjs を使いました。 .mjs は bun でも動きます。(Bunは.js, .ts, .cjs, .mjs, .jsx, .tsxファイルを直接実行できます。あー楽ちん。)

まず、recursive設定の無い普通のコード。
```js:recursive無し readdir-recursive-none.mjs
console.time('readdir')
import { readdir } from "fs/promises";
const results = await readdir('/home/tato/bun/readdir/mydir1');
console.log('count: ', results.length);
console.log(results); // ["a.js", "b/c.js" ...
console.timeEnd('readdir')
```

次に、{ recursive: true } 設定のあるコード。

'＜ディレクトリへのパス＞'の部分は、__dirnameがCommon JSでしかサポートされておらず、ES Moduleだと別途モジュール読み込まないといけないという事で手を抜きました。はい。bunならimport と __dirname まぜても使えるのになぁ。

```js:recursive: true有り readdir-recursive-none.mjs
console.time('readdir')
import { readdir } from "fs/promises";
const results = await readdir('＜ディレクトリへのパス＞', { recursive: true });
console.log('count: ', results.length);
console.log(results); // ["a.js", "b/c.js" ...
console.timeEnd('readdir')
```


では結果を見てみましょう。まずは、Node.jsから。

```sh: Node.js で recursive-true 有りと無しを実行
$ node readdir-recursive-true.mjs #有り
[
  'mydir2',        'mydir2/...',   'mydir2/dir1',  'mydir2/dir10',
  'mydir2/dir100', 'mydir2/dir11', 'mydir2/dir12', 'mydir2/dir13',
  'mydir2/dir14',  'mydir2/dir15', 'mydir2/dir16', 'mydir2/dir17',
  'mydir2/dir18',  'mydir2/dir19', 'mydir2/dir2',  'mydir2/dir20',
  'mydir2/dir21',  'mydir2/dir22', 'mydir2/dir23', 'mydir2/dir24',
  'mydir2/dir25',  'mydir2/dir26', 'mydir2/dir27', 'mydir2/dir28',
  'mydir2/dir29',  'mydir2/dir3',  'mydir2/dir30', 'mydir2/dir31',
  'mydir2/dir32',  'mydir2/dir33', 'mydir2/dir34', 'mydir2/dir35',
  'mydir2/dir36',  'mydir2/dir37', 'mydir2/dir38', 'mydir2/dir39',
  'mydir2/dir4',   'mydir2/dir40', 'mydir2/dir41', 'mydir2/dir42',
  'mydir2/dir43',  'mydir2/dir44', 'mydir2/dir45', 'mydir2/dir46',
  'mydir2/dir47',  'mydir2/dir48', 'mydir2/dir49', 'mydir2/dir5',
  'mydir2/dir50',  'mydir2/dir51', 'mydir2/dir52', 'mydir2/dir53',
  'mydir2/dir54',  'mydir2/dir55', 'mydir2/dir56', 'mydir2/dir57',
  'mydir2/dir58',  'mydir2/dir59', 'mydir2/dir6',  'mydir2/dir60',
  'mydir2/dir61',  'mydir2/dir62', 'mydir2/dir63', 'mydir2/dir64',
  'mydir2/dir65',  'mydir2/dir66', 'mydir2/dir67', 'mydir2/dir68',
  'mydir2/dir69',  'mydir2/dir7',  'mydir2/dir70', 'mydir2/dir71',
  'mydir2/dir72',  'mydir2/dir73', 'mydir2/dir74', 'mydir2/dir75',
  'mydir2/dir76',  'mydir2/dir77', 'mydir2/dir78', 'mydir2/dir79',
  'mydir2/dir8',   'mydir2/dir80', 'mydir2/dir81', 'mydir2/dir82',
  'mydir2/dir83',  'mydir2/dir84', 'mydir2/dir85', 'mydir2/dir86',
  'mydir2/dir87',  'mydir2/dir88', 'mydir2/dir89', 'mydir2/dir9',
  'mydir2/dir90',  'mydir2/dir91', 'mydir2/dir92', 'mydir2/dir93',
  'mydir2/dir94',  'mydir2/dir95', 'mydir2/dir96', 'mydir2/dir97',
  ... 2 more items
]
readdir: 20.435ms

$ node readdir-recursive-none.mjs #無し
count:  1
[ 'mydir2' ]
readdir: 10.321ms
```

 { recursive: true }では簡単に再帰的に下のディレクトリまで拾っていますね。

次に、Bunでやってみます。


```sh: Node.js で recursive-true 有りと無しを実行
$ bun readdir-recursive-true.mjs #有り
[
  "mydir2", "mydir2/dir91", "mydir2/dir58", "mydir2/dir87", "mydir2/dir39", "mydir2/dir92", "mydir2/dir63",
  "mydir2/dir42", "mydir2/dir62", "mydir2/dir79", "mydir2/dir75", "mydir2/dir22", "mydir2/dir88",
  "mydir2/dir44", "mydir2/dir89", "mydir2/...", "mydir2/dir64", "mydir2/dir19", "mydir2/dir97",
  "mydir2/dir41", "mydir2/dir26", "mydir2/dir95", "mydir2/dir2", "mydir2/dir69", "mydir2/dir8",
  "mydir2/dir73", "mydir2/dir52", "mydir2/dir53", "mydir2/dir83", "mydir2/dir36", "mydir2/dir33",
  "mydir2/dir54", "mydir2/dir93", "mydir2/dir71", "mydir2/dir67", "mydir2/dir84", "mydir2/dir56",
  "mydir2/dir21", "mydir2/dir90", "mydir2/dir48", "mydir2/dir70", "mydir2/dir96", "mydir2/dir4",
  "mydir2/dir74", "mydir2/dir81", "mydir2/dir7", "mydir2/dir15", "mydir2/dir82", "mydir2/dir25",
  "mydir2/dir99", "mydir2/dir23", "mydir2/dir43", "mydir2/dir31", "mydir2/dir38", "mydir2/dir9",
  "mydir2/dir10", "mydir2/dir12", "mydir2/dir45", "mydir2/dir40", "mydir2/dir86", "mydir2/dir55",
  "mydir2/dir17", "mydir2/dir60", "mydir2/dir6", "mydir2/dir27", "mydir2/dir29", "mydir2/dir13",
  "mydir2/dir66", "mydir2/dir85", "mydir2/dir47", "mydir2/dir65", "mydir2/dir18", "mydir2/dir78",
  "mydir2/dir80", "mydir2/dir34", "mydir2/dir16", "mydir2/dir3", "mydir2/dir50", "mydir2/dir14",
  "mydir2/dir28", "mydir2/dir98", "mydir2/dir61", "mydir2/dir32", "mydir2/dir24", "mydir2/dir5",
  "mydir2/dir46", "mydir2/dir37", "mydir2/dir72", "mydir2/dir51", "mydir2/dir94", "mydir2/dir76",
  "mydir2/dir1", "mydir2/dir35", "mydir2/dir59", "mydir2/dir77", "mydir2/dir68", "mydir2/dir100",
  "mydir2/dir30", "mydir2/dir11", "mydir2/dir20", "mydir2/dir57", "mydir2/dir49"
]
[1.37ms] readdir

$ bun readdir-recursive-none.mjs #無し
count:  1
[ "mydir2" ]
[0.67ms] readdir
```

このベンチでは、Bunの方が40倍まではいかなかったけど、それでもやはり15倍くらい爆速でした。めでたし。



# 最近 Qiita に書いた Bun 関連の記事10選

https://qiita.com/advent-calendar/2023/bunjs

https://qiita.com/toshirot/items/f6bd561bc2522f1d26e0

https://qiita.com/toshirot/items/a5dc53ef191d20bbeb42

https://qiita.com/toshirot/items/74476af27e67d8534c5a

https://qiita.com/toshirot/items/94961b6dde72be80ab0c

https://qiita.com/toshirot/items/7a0555b46b3a64a4f643

https://qiita.com/toshirot/items/c4d090bc38f9b85d8cdd

https://qiita.com/toshirot/items/4e8e571dd50a45c651f0

https://qiita.com/toshirot/items/e4eb635596ae4f63bdfd

https://qiita.com/toshirot/items/a81f2073e373b0351493

https://qiita.com/toshirot/items/06a992af8cf8aca9ff95