# Your GDScript arrays are slower than you think — I built a CLI that detects the pitfalls and benchmarks them

> Source: <https://dev.to/sunnydachs/your-gdscript-arrays-are-slower-than-you-think-i-built-a-cli-that-detects-the-pitfalls-and-3ok3>
> Published: 2026-09-20 02:33:36+00:00

"Why is my enemy loop stuttering? I'm not even doing anything heavy here."

Every Godot tutorial tells you to just use arrays. Nobody tells you that an

untyped array holds Variants, that a sequential-key Dictionary is ~2x slower

than a plain Array, or that the official docs contradict themselves about

packed arrays. This is about **gd-bench**, a CLI I built that scans your

`.gd` files for array-type performance pitfalls and generates runnable Godot

micro-benchmarks for each one — with zero dependencies and no LLM anywhere.

[https://github.com/sunnydachs/gd-bench](https://github.com/sunnydachs/gd-bench)

Point it at a Godot project. It scans every `.gd` file and reports where the

array-type choice is costing you performance, then writes a runnable

benchmark script per finding so you can measure before you rewrite.

```
pip install gd-bench

gd-bench .              # scan the current project
gd-bench . --gen-bench  # also generate Godot micro-benchmarks per finding
```

Example output (a small demo project, 7 files):

``` js
game/main.gd:4  🐢 UNTYPED ARRAY
    var inventory = []            # untyped array — Variant soup
    ->
game/main.gd:5  🔧 TYPED ARRAY
    var scores: Array[int] = []   # typed array — fine but Packed is faster
    ->
game/main.gd:6  ✅ PACKED ARRAY
    var points: PackedVector2Array = PackedVector2Array()  # already optimal
    ->

summary: {"typed_array": 5, "dict_as_array": 2, "untyped_array": 1, "packed_array": 3}
```

It reports 4 kinds of findings:

`var x = [1, 2, 3]` holds Variants; slowest to iterate`Array[int]` gives compile-time checks but is still
Variant-backed; `PackedInt32Array` is faster for the same element type`var d = {}` filled with sequential keys is ~2x slower
and uses about half the memory again versus a plain Array
A static type checker will never tell you which of these is actually slower

in *your* loop — and the official docs can't agree either. Only a

measurement can.

This was the core design decision. A linter could just say "use

`PackedInt32Array` here" and be done. But I deliberately built it as

**scanner + benchmark generator** instead. Three reasons:

Deterministic work deserves deterministic tools.

The detail I obsessed over: the tool must never hand you a rewrite suggestion

it hasn't equipped you to verify. So the rule is:

That single line (`書き換え前に必ず計測` / "always measure before rewriting")

is what keeps the tool from becoming another "just use X" voice. The

generated script is plain GDScript (`extends SceneTree`, runs headless) so

you can run it in CI or on a machine without the editor:

```
godot --headless --script gd-bench-out/bench_gd_untyped_array_4.gd
# gd-bench: untyped_array
#   untyped_array: 8231 usec
#   typed_array: 5120 usec
#   ratio: 1.61x
```

Not a toy example — the detection patterns come from the same shapes real

Godot projects and issues use:

`DICT AS ARRAY` cases that were doing 1000 sequential-key inserts per frame
The real win wasn't just "it works" — it's that the tool's advice and the

docs' advice can be *compared* in one command, which is exactly what the

godot-docs issue is asking for.

`var` lines, `{}` + indexed assignment); it does not catch arrays built
through multi-line expressions or passed as literals into function calls.
Frame time scales with your worst inner loop. Array-type choice is exactly

the class of problem where "scanner + generated measurement" earns its keep:

the advice is cheap to give, but only a benchmark tells you if it was true

for your code.

This is one in a series on the same principles — read-only, deterministic,

zero-dependency CLIs that detect drift between what your code implies and

what it does. Siblings: **dep-triage** (Dependabot PR cleanup),

**pivot-diag** (Excel pivot audits), **doc-drift** (README code-example

drift), **plan-drift** (tracking plan ↔ implementation), **oas-drift**

(OpenAPI spec ↔ code).

Feedback, and especially *false-positive reports from your own Godot projects*, are the most useful thing you can give me — drop them in the repo
