npm install took 21.77 seconds cold and 3.2 seconds warm
In this article
npm install took 21.77 seconds with an empty cache and 3.2 seconds under the same conditions the second time.
A 6.8x difference sounds like a simple comparison, but the path from four dependencies—Express, lodash, chalk, and axios—in package.json to 80 packages shows that npm itself is not the only thing doing work. I want to repeat this with different storage locations. I initially thought the job would be lighter. Then I counted node_modules: it had grown to 2,027 files and 6058033 bytes. The visible four dependencies did not mean four files were fetched and expanded.
I measured this on July 4, 2026, on a Surface Pro 11th Edition with ARM64 Windows. The warm result is still a real extraction wait, and the date in the record is 2026-07-04. Node.js was 24.13.0. The machine was connected to AC power, with the default Balanced power plan unchanged.
Start with a small package.json
The only direct dependencies were express, lodash, chalk, and axios. I had been influenced by how small the package.json looked, but the expanded installation was a different size. I used npm install with --no-audit --no-fund to reduce extra network activity and separated the npm cache for the measurement.
npm install --cache "...\npmtest\.npmcache" --no-audit --no-fund
Cold means the npm cache was empty. Warm means creating node_modules again with the same cache. I wanted to see the difference between the initial network wait and the second extraction cost. If the goal were CPU-only performance, this method would mix in too many things, but it is close to an everyday wait.
This was not designed to measure CPU speed. npm install combines network access, archive downloads, dependency resolution, and the creation of many small files. Cold runs tend to be dominated by waiting, so they are not a good basis for talking about ARM64 versus x64.
Four dependencies become 80 packages
This was the installation result:
| Condition | elapsed_s | node_modules_files | node_modules_bytes | npm output |
|---|---|---|---|---|
| cold | 21.77 seconds | 2027 | 6058033 bytes | added 80 packages in 21s |
| warm | 3.2 seconds | 2027 | 6058033 bytes | added 80 packages in 3s |
Four dependencies became 80 packages and 2,027 files. Seeing that changed the meaning of 3.2 seconds. Warm is not merely “fast because it is the second run”; it is close to the time needed to take cached content and put it back as a large number of small files. The filesystem side was more prominent than the CPU side.
This is not determined by integer arithmetic on the CPU alone. It exposes the load of repeatedly creating small files in the filesystem. The characteristic weight of node_modules cannot be explained by dependency count alone. Deleting and recreating node_modules can feel unexpectedly heavy; the lockfile is not the whole story.
I also measured a Node.js loop
The same JSON included a Node.js loop benchmark: an integer remainder loop and a BigInt version, each run three times.
| Benchmark | Run 1 | Run 2 | Run 3 |
|---|---|---|---|
| node_int_loop | 238ms | 195ms | 196ms |
| node_bigint | 1968.4358ms | 1737.8546ms | 1811.2391ms |
The ordinary integer loop stayed between 195ms and 238ms. The BigInt version was 1737.8546ms to 1968.4358ms, in a different order of magnitude. At a glance it looks about 8x to 10x slower.
I stopped at that point. The table invites the simple statement that BigInt is heavy, but checking the output values changed the interpretation. If I had written only the ratio, I would have misread the measurement.
The loop counts were not equal
The failure was that the ordinary and BigInt versions did not run the same number of iterations. The output values were 599999994 for the ordinary version and 119999995 for BigInt. Because the loop adds remainders, the BigInt version effectively ran fewer iterations than the ordinary version. I had treated loop-count details too casually until I looked closely at the actual behavior.
Dividing only the elapsed times underestimates the cost of BigInt. The values were 238ms, 195ms, and 196ms for the ordinary version, and 1968.4358ms, 1737.8546ms, and 1811.2391ms for BigInt. Correcting for the different iteration counts gives roughly the 1ns range for the ordinary loop and the 40ns range for BigInt. Looking at the three-run averages, BigInt is about 44x heavier.
The counts should have been equal from the start. I got stuck on this briefly until noticing it after the measurement. If I had looked only at the integer-loop table and written “BigInt is about 9x slower,” the article would have understated the real difference considerably.
Cold is download wait; warm is extraction wait
The empty npm cache is a major part of the 21.77-second cold result. Still, cache state alone is too coarse an explanation. stdout also reported added 80 packages in 21s, which includes package-download waiting. That makes it weak evidence for Snapdragon X Elite CPU performance. I want to repeat the measurement with different storage locations.
The 3.2-second warm result was much closer to a practical number in my environment. The cache was warm, yet creating 2,027 files still took 3.2 seconds. Many small files are a more everyday workload than a simple loop. In normal development, this may be the kind of wait that matters more.
The Node.js integer loop was fast: even a roughly 200-million-iteration operation finished around 200ms. npm install is not determined by that. I later wondered why four dependencies expanded so much. Files and directories multiply, and antivirus or synchronization behavior can enter from the side. Those mixed operations are closer to what development actually waits on.
Pause before deleting
In CI, keeping the npm cache is worthwhile. If 21.77 seconds becomes 3.2 seconds, the savings accumulate even in short jobs. This difference appeared in a small project with only four direct dependencies, so it is hard to ignore in a repository with more. Filesystem creation, rather than CPU speed, is the dominant-looking cost here.
Locally, I now pause before casually deleting node_modules. Deleting it is convenient when something is broken, but recreating 2,027 files has a cost, so I first look at the cache and lockfile. Even with a cache, 3.2 seconds repeated many times breaks the working rhythm. The lockfile is not the whole story.
The conclusion is not simply that npm install works on ARM64 Windows. The interesting part is that four dependencies expanded into 80 packages, 2,027 files, and 6058033 bytes. Next I would separate the effects of cache state, antivirus, and a work folder outside OneDrive rather than merely increasing the dependency count.