---
url: /blog-program/for-each-and-for-loop.md
description: 常聽到別人說特定情境下 forEach 比 for 迴圈還慢，剛好有個好機會，讓我來試試看吧。ԅ(´∀` ԅ)
---

![for-each-and-for-loop](/for-each-and-for-loop.webp){.cover}

# 聽說 forEach 比 for 迴圈慢？

常聽到別人說：某些情況下 `forEach` 比 `for` 迴圈還慢。

babylon.js 的 Particle System 文件中，[更新大量粒子](https://doc.babylonjs.com/features/featuresDeepDive/particles/particle_system/customizingParticles/)的迴圈預設也都是使用 `for` 迴圈。

最近酷酷的元件做了一個[下雪背景](https://chillcomponent.codlin.me/components/bg-snow/)，粒子數量與計算量相對得多。

剛好藉這個機會來實測看看不同迴圈的實際效果。%ԅ(´∀\` ԅ)%

::: tip
以下實驗基於 Google Chrome v131.0.6778.110

超不嚴謹，僅針對此元件情境，請不要當作正式結論。( ﾟ∀。)
:::

## 實測

基於原本的[程式碼](https://gitlab.com/side_project/chill-component/-/blob/main/src/components/bg-snow/bg-snow-worker.ts?ref_type=heads#L97)，加上 `performance.now` 計算執行時間，且每運行 100 次再輸出一次，避免 devtool 炸掉。

```ts
let count = 0
particleSystem.updateFunction = function (particles) {
  const startTime = performance.now()

  // 計算 staticMap 與粒子是否有碰撞
  for (const id in staticMap) {
    // ...
  }

  const endTime = performance.now()

  if (count % 100 === 0) {
    console.log({
      particles: particles.length,
      time: endTime - startTime,
    })
  }
  count++
}
```

### for

```text
{particles: 1159, time: 0.5}
{particles: 1185, time: 1}
{particles: 1170, time: 0.5999999046325684}
{particles: 1195, time: 1}
{particles: 1200, time: 1.2000000476837158}
{particles: 1194, time: 0.7000000476837158}
{particles: 1201, time: 0.6999999284744263}
{particles: 1182, time: 1.1999999284744263}
```

可以看到原本粒子數量最多大約在 1200 左右，執行時間在 0.5 ~ 1.2 ms 之間。

### forEach

現在來看看 `forEach` 的執行時間。

::: tip
staticMap 的 `for` 部分維持不變，因為這次只比較矩陣 `forEach` 與 `for` 的差異。
:::

```ts
particleSystem.updateFunction = function (particles) {
  const startTime = performance.now()
  // 計算 staticMap 與粒子是否有碰撞
  for (const id in staticMap) {
    const bounding = staticMap[id]
    if (!bounding)
      continue

    /** 依照文件作法更新粒子
     *
     * https://doc.babylonjs.com/features/featuresDeepDive/particles/particle_system/customizingParticles/
     */
    particles.forEach((particle, index) => {
      // ...
    })
  }

  // ...
}
```

來看看結果

```text
{particles: 1163, time: 1.100000023841858}
{particles: 1168, time: 1.2000000476837158}
{particles: 1184, time: 1.2000000476837158}
{particles: 1180, time: 1}
{particles: 1171, time: 1}
{particles: 1161, time: 1.2000000476837158}
{particles: 1174, time: 1.1999999284744263}
{particles: 1166, time: 1.2999999523162842}
```

可以注意到耗時多了一點點，執行時間在 1 ~ 1.3 ms 之間。

### for of

再來試試看 `for of`。

```ts
particleSystem.updateFunction = function (particles) {
  const startTime = performance.now()
  // 計算 staticMap 與粒子是否有碰撞
  for (const id in staticMap) {
    const bounding = staticMap[id]
    if (!bounding)
      continue

    /** 依照文件作法更新粒子
     *
     * https://doc.babylonjs.com/features/featuresDeepDive/particles/particle_system/customizingParticles/
     */
    let index = -1
    for (const particle of particles) {
      index++
      // ...
    }
  }

  // ...
}
```

結果：

```text
{particles: 1189, time: 1.2000000476837158}
{particles: 1186, time: 1.5}
{particles: 1185, time: 1.5}
{particles: 1182, time: 1.100000023841858}
{particles: 1190, time: 0.7999999523162842}
{particles: 1188, time: 1.100000023841858}
{particles: 1187, time: 1.399999976158142}
{particles: 1185, time: 2.1999999284744263}
{particles: 1181, time: 1.1999999284744263}
{particles: 1186, time: 1}
```

和 `for` 差不多，但是偶而會跑出 2 ms 的時間。

## 暴風雪！

差異不大好像不夠精彩，讓我們把粒子數量加好加滿，變成暴風雪看看！ლ（´∀\`ლ）

![snowstorm](/for-each-and-for-loop/snowstorm.png)

超多雪！ᕕ( ﾟ ∀。)ᕗ

`for`

```text
{particles: 97970, time: 360.10000002384186}
{particles: 98122, time: 340}
{particles: 98118, time: 342.7000000476837}
{particles: 98035, time: 355.2000000476837}
{particles: 98069, time: 348.60000002384186}
```

`forEach`

```text
{particles: 98275, time: 304.09999990463257}
{particles: 98491, time: 273.5}
{particles: 98092, time: 356}
{particles: 97463, time: 472.7000000476837}
{particles: 98080, time: 342}
```

`for of`

```text
{particles: 99365, time: 122.20000004768372}
{particles: 99052, time: 181.59999990463257}
{particles: 98330, time: 289.3000000715256}
{particles: 97579, time: 390.7999999523163}
{particles: 98233, time: 329.3000000715256}
```

粒子數量大約在 98k 左右，可以看到結果很有趣：

* `for` 與 `forEach` 時間差不多，但是 `for` 比較穩定
* `for of` 最快，但偶而會變慢。

以上就是這次的超不專業的不嚴謹實驗。੭ ˙ᗜ˙ )੭

究竟誰比較快，會因為情境、runtime、瀏覽器最佳化與程式寫法而有所不同，最好應該實際跑過 benchmark 再做選擇。

## 總結 🐟

* 元素數量不多時，`forEach` 與 `for` 差異不大。
* 性能會因情境、runtime、瀏覽器最佳化與程式寫法而有所不同，最好應該實際跑過 benchmark 再做選擇。
