DEV Community

Cover image for Part 1 — What Actually Happens When Code Runs
Alok Kumar
Alok Kumar

Posted on

Part 1 — What Actually Happens When Code Runs

When we write:

const result = add(10, 20);
Enter fullscreen mode Exit fullscreen mode

it feels like the computer simply "runs the code."

But the CPU doesn't understand JavaScript. There are several layers between the code we write and the hardware actually executing instructions.

That's what I wanted to understand first.


From JavaScript to the CPU

In Node.js, JavaScript is handled by V8, the JavaScript engine.

A simplified view looks like this:

JavaScript
    ↓
V8
    ↓
Bytecode
    ↓
JIT compilation
    ↓
Machine instructions
    ↓
CPU
Enter fullscreen mode Exit fullscreen mode

V8 doesn't simply "interpret JavaScript" or "compile JavaScript" once and forget about it.

It can start with bytecode and progressively compile frequently executed ("hot") code into more optimized machine code.

Eventually, the CPU is executing instructions that operate at a much lower level than the JavaScript we originally wrote.


What does the CPU actually do?

At its core, a CPU repeatedly executes instructions.

A simplified mental model is:

Fetch → Decode → Execute → Repeat
Enter fullscreen mode Exit fullscreen mode

The CPU has several important pieces involved in this process.

Registers are tiny, extremely fast storage locations inside the CPU. They're used to hold values the CPU is actively working with.

The ALU (Arithmetic Logic Unit) performs many arithmetic and logical operations.

The Program Counter (PC) keeps track of where the next instruction comes from.

And the CPU runs according to a clock, measured in GHz. A 3 GHz CPU has roughly 3 billion clock cycles per second, but that does not mean it executes 3 billion instructions per second. Different instructions and architectures have different costs.

Modern CPUs are far more sophisticated than this simplified model, using pipelining, multiple execution units, branch prediction, out-of-order execution, and more. But the basic model is enough to start reasoning about performance.


The CPU doesn't get everything from RAM

One of the most important things I learned here is that where data lives matters.

A simplified hierarchy looks like:

Registers
    ↓
L1 Cache
    ↓
L2 Cache
    ↓
L3 Cache
    ↓
RAM
    ↓
SSD
    ↓
Network
Enter fullscreen mode Exit fullscreen mode

This isn't a literal pipeline that every access travels through. It's a hierarchy of increasingly larger but generally slower resources.

Registers are extremely close to the execution units and very limited in size.

Caches sit between the CPU and RAM and keep frequently or recently used data closer to the CPU.

RAM is much larger, but accessing it takes significantly more time than accessing a register or cache.

This leads to a fundamental performance idea:

The closer the data is to the CPU, the cheaper it generally is to access.


Cache hits, misses and locality

Suppose the CPU needs some data.

If it's already in a cache:

CPU → Cache → Data found
Enter fullscreen mode Exit fullscreen mode

that's a cache hit.

If it isn't:

CPU → L1 → L2 → L3 → RAM
Enter fullscreen mode Exit fullscreen mode

the CPU may have to go farther to find it. That's a cache miss.

This is why locality matters.

Temporal locality: if we accessed something recently, we're likely to access it again.

Spatial locality: if we accessed one memory location, we're likely to access nearby locations.

For example:

for (let i = 0; i < array.length; i++) {
    sum += array[i];
}
Enter fullscreen mode Exit fullscreen mode

accesses elements sequentially. That pattern is generally friendlier to caches than jumping randomly around memory.

This is also why two pieces of code with the same O(n) complexity can have very different real-world performance.


Latency vs Bandwidth

These two terms show up everywhere in computer systems.

Latency is essentially:

How long until I get the result?

Bandwidth is:

How much data can I transfer per unit of time?

A system can have high bandwidth but still have significant latency.

We'll encounter this distinction again with RAM, disks, networks, and databases.


What about numbers?

At the lowest level, computers work with bits:

0 or 1
Enter fullscreen mode Exit fullscreen mode

Eight bits make a byte.

A fixed number of bits means a fixed number of representable values. That's why we have concepts like:

  • signed vs unsigned integers
  • integer overflow
  • limited precision

Floating-point numbers introduce another interesting problem.

JavaScript's Number uses IEEE-754 double-precision floating-point representation. Not every decimal fraction has an exact binary representation.

That's why:

0.1 + 0.2
Enter fullscreen mode Exit fullscreen mode

produces:

0.30000000000000004
Enter fullscreen mode Exit fullscreen mode

It's not that JavaScript can't do basic addition. We're seeing the consequences of how numbers are represented in binary with finite precision.


CPU-bound vs I/O-bound

Another useful distinction is whether our program is primarily computing or waiting.

A huge calculation:

for (let i = 0; i < 5_000_000_000; i++) {
    // heavy computation
}
Enter fullscreen mode Exit fullscreen mode

is CPU-bound. The CPU spends most of its time doing work.

But something like:

await fetch("/users");
Enter fullscreen mode Exit fullscreen mode

is largely I/O-bound. The program may spend significant time waiting for the network rather than continuously using the CPU.

This distinction becomes particularly important when we get to Node.js and its event loop.


Putting it all together

So when I run a Node.js function, the mental model I want to have is no longer:

JavaScript → magic → result
Enter fullscreen mode Exit fullscreen mode

but something closer to:

JavaScript
    ↓
V8
    ↓
Bytecode / JIT compilation
    ↓
Machine instructions
    ↓
CPU core
    ↓
Registers
    ↓
Caches
    ↓
RAM
Enter fullscreen mode Exit fullscreen mode

Along the way, performance can be affected by things like:

instruction execution, CPU utilization, memory latency, cache misses, locality, and I/O waits.

And that's the foundation for everything that follows.

Because the next question is obvious:

If multiple programs are running on the same machine, who decides which program gets the CPU, how memory is managed, and how programs interact with hardware?

That's where the Operating System comes in.

Top comments (0)