Computer
The cs600 covers the rudimentary elements of Computer Science and aims to enhance the work-related self-efficacy of someone only with a degree in Mathematics. This first post traces the arc from mechanical calculators to modern accelerators, covering the hardware that underpins all computation.
I
1.1. Computer
Charles Babbage’s analytical engine (1837) is widely regarded as the first conceptual design of a general-purpose computer. It separated a processing unit from memory, supported conditional branching and loops via three types of punch cards, and was Turing-complete by modern definitions. Ada Lovelace wrote what is considered the first computer program for the machine (an algorithm for computing Bernoulli numbers, published as Note G to her 1843 translation of Menabrea’s paper on the engine). A century later, the Turing machine was introduced in Turing (1936), an abstract device that formalised the limits of what machines can compute, resolved the Entscheidungsproblem negatively, and laid the theoretical foundation for modern computer science.
The Electronic numerical integrator and computer (ENIAC), completed in 1945 by J. Presper Eckert and John Mauchly is perhaps the first programmable electronic general-purpose digital computer. It used 17,468 vacuum tubes, occupied a 50×30-foot room, consumed ~150 kW, and performed up to 5,000 additions per second, which is orders of magnitude faster than electromechanical predecessors. Von Neumann (1945) then described the stored-program concept, the idea of storing both instructions and data in the same uniform memory, via the Von Neumann architecture that still dominates computer design.
The transistor, demonstrated in 1947 by Bell Labs (Nobel Prize in Physics, 1956), replaced vacuum tubes with devices which were smaller, faster, more reliable, and consumed far less power. The next leap came with the integrated circuit (IC). Jack Kilby at Texas Instruments showed the first working IC on a germanium substrate in 1958 (Nobel Prize in Physics, 2000), while Robert Noyce at Fairchild Semiconductor independently devised the planar silicon IC with aluminium metallisation (January 1959) that enabled mass production. The IC’s key innovation was consolidating transistors, resistors, and capacitors onto a single semiconductor substrate.
Moore’s Law drove successive waves of integration. Moore (1965) observed that the number of transistors per IC doubled roughly every year (i.e. revised to every two in 1975), an empirical trend that guided the semiconductor roadmap for five decades. Having said that, the Intel 4004 (1971) was the first commercial microprocessor, which packed 2,300 transistors into a 4-bit CPU on a 12 mm² die, and was originally commissioned by Busicom (i.e. the Japanese calculator maker). The IBM PC (1981), built with an open architecture and Intel’s 8088 processor, standardised personal computing and spawned the clone ecosystem that persists today.
Clock speed plateaued at ~3-4 GHz around 2004 as Dennard scaling broke down. The industry moved to acquire multiple cores on a single die at lower frequencies within the same power envelope, while a system on chip (SoC) paradigm introduced a unified design where core components and specialised accelerators (e.g. NPU - inference, ISP - camera) all live on a die. For example, Apple Silicon (M1, 2020) places 8 CPU cores, 8 GPU cores, a 16-core Neural Engine, and LPDDR memory dies co-packaged on the same substrate (system-in-package) at 5 nm. Desktops and servers indeed prefer wiring components onto a motherboard for upgradability, connecting the hardware peripherals via buses and chipsets.
1.2. Central Processing Unit
A central processing unit (CPU) implements an instruction set architecture (ISA), the set of predefined operations the hardware could perform. Every computation reduces to a sequence of these instructions in which dedicated units within the CPU execute them. An arithmetic logic unit (ALU) performs integer and bitwise logic operations, a floating-point unit (FPU) performs IEEE 754 arithmetic, and a control unit (CU) sequences instruction execution and routes data between them, as the diagram illustrates a minimal 4-bit CPU with a 16-instruction ISA. The two dominant ISA families reflect a fundamental trade-off between simplicity and density of instructions.
Intel’s x86 (1978) exemplifies complex instruction set computer (CISC): variable-length instructions (1-15 bytes), memory operands in arithmetic, and complex addressing modes accumulated 1,500+ instructions in which backwards compatibility forbids removing legacy ones. Whereas, Acorn RISC Machine (ARM, 1985) has reduced instruction set computer (RISC): fixed-length instructions, a load-and-store architecture, and a large register file, resulting in only ~350 base instructions. RISC-V (2010) pushes the RISC philosophy further with a minimal base integer ISA (~47 instructions) and optional extensions, released royalty-free via UC Berkeley.
The practical consequence of the CISC design is decode complexity. x86’s variable-length format forces the decoder to inspect each byte sequentially to find instruction boundaries, and modern x86 CPUs from both vendors dedicate substantial die area to front-end decoders that crack these into fixed-width micro-ops ($\mu$ops), effectively translating CISC to RISC internally. ARM’s fixed 4-byte format eliminates this overhead, yielding simpler decode logic, shorter pipelines, and lower power per instruction retired. This efficiency gap drove Apple’s transition to ARM-based Apple Silicon (M1-M4).
Every CPU runs the same fetch-decode-execute cycle: fetch the instruction at the PC $\to$ decode it $\to$ execute on the appropriate unit $\to$ write back. While there are various types of registers (e.g. PC, SP, GPRs, flags), a CPU’s word size is the width of its GPRs in bits, which typically matches the ALU and data path width. Modern memory is byte-addressable, meaning each address refers to a single byte (8 bits), and an $N$-bit CPU can address $2^N$ bytes (e.g. $2^{32}$ = 4 GB for 32-bit, $2^{64}$ $\approx$ 16 EB for 64-bit). A variable wider than one byte (e.g. a 4-byte integer) occupies consecutive addresses, and its pointer holds the address of the first byte. Addresses are conventionally written in hexadecimal (base-16, prefixed 0x), since each hex digit maps to exactly 4 bits, making binary patterns compact and readable.
Pipelining increases throughput by overlapping execution stages, so after the pipeline fills one instruction completes per cycle rather than one per $k$ cycles for a $k$-stage pipeline. Out-of-order execution, first realised in Tomasulo’s algorithm (1967), keeps the pipeline fed by dispatching ready instructions regardless of program order via register renaming and reservation stations. Branch prediction guesses the direction of conditional branches (95-99% accuracy, 10-20 cycle penalty on misprediction). Speculative execution runs past unresolved branches, rolling back on misprediction. The latter was exploited by the Meltdown & Spectre (2018) vulnerabilities.
1.3. Random Access Memory
Registers are fast but scarce (a few KB per core), so computer memory forms a hierarchy trading off speed, capacity, and cost. A cache hierarchy of L1 (~1 ns, per core), L2 (~3-10 ns, per core or shared), and L3 (~10-20 ns, shared) is built from static RAM (SRAM). Off-chip main memory uses dynamic RAM (DRAM), which stores each bit in a single transistor plus capacitor, far denser and cheaper but slower (~50-100 ns). A well-designed program achieves high temporal and spatial locality, accessing the same data repeatedly and accessing contiguous addresses, to minimise cache misses that cascade through successively slower levels.
When multiple cores cache the same physical address, one core’s write can desync the others. Cache coherence protocols keep them aligned by broadcasting updates. The MESI protocol assigns each cache line one of four states: {Modified, Exclusive, Shared, Invalid} and uses bus snooping or directory-based schemes to maintain consistency. False sharing occurs when unrelated data on the same cache line (typically 64 bytes) causes coherence traffic between cores. The overhead scales with core count, making coherence a limiting factor in shared-memory parallelism.
Allocating variable-sized blocks of physical memory leads to fragmentation, so virtual memory partitions both address spaces and physical RAM into fixed-size pages and interposes a hardware translation layer. The memory management unit (MMU) in the CPU translates every virtual address to a physical frame number by consulting a page table. The translation lookaside buffer (TLB) caches recent translations, resolving most lookups in 1-2 cycles. A TLB miss triggers a hardware page table walk (4 memory reads on x86-64); if the page has no physical backing, the CPU raises a page fault exception that the OS kernel handles (§603). This indirection isolates processes, enables overcommitment, and eliminates external fragmentation.
1.4. Persistent Storage
Persistent storage retains data without power. Hard disk drives (HDDs) store data magnetically on spinning platters with read/write heads on an actuator arm. Performance is dominated by mechanical delays: seek time (~10 ms average) for the head to reach the correct track, and rotational latency (half the rotation period, ~4.2 ms at 7,200 RPM) for the desired sector to pass under the head. Sequential throughput reaches 100-200 MB/s, but random access is severely limited by these physical movements. By the 2010s, driven by falling NAND prices and Intel’s early consumer SSDs, SSDs had largely replaced HDDs for primary storage.
Solid-state drives (SSDs) use NAND flash memory, where each cell is a transistor with an electrically isolated floating gate. Cells are organised into pages (~4-16 KB, the unit of read/write) and blocks (256-512 pages, the unit of erase). This asymmetry, where data is programmed at page level but erased at block level, necessitates garbage collection (relocating valid pages from partially-stale blocks, then erasing them) and the TRIM command (letting the OS notify the SSD when files are deleted). Each cell has a limited number of program/erase cycles (1K-100K, fewer as more bits are packed per cell), so wear leveling distributes writes evenly across the device.
II
2.1. Graphics Processing Unit
Flynn’s taxonomy classifies processor architectures by instruction and data streams. A single-core CPU is SISD (Single Instruction, Single Data), while multi-core CPUs operate as MIMD (Multiple Instruction, Multiple Data). SIMD (Single Instruction, Multiple Data) extends a single core with vector units (e.g. SSE, AVX) that apply one instruction to multiple data elements simultaneously. GPUs take this further with SIMT, essentially SIMD combined with multithreading, where groups of threads (warps in CUDA, wavefronts in AMD) execute the same instruction in lockstep on different data.
Early GPUs were fixed-function hardware for graphics rendering, hardwiring vertex transformation, lighting, rasterisation, and texturing with no programmability. NVIDIA’s GeForce 256 (1999), marketed as the world’s first GPU, moved hardware transform and lighting onto the graphics card but kept the pipeline fixed. Programmable shaders arrived with the GeForce 3 (2001), and the decisive shift came with the Tesla microarchitecture (2006) whose GeForce 8800 GTX was the first GPU with unified shaders, merging vertex and pixel processors into general-purpose streaming processors so that no fixed-function stage sat idle when another was saturated (128 SPs across 16 SMs, 681M transistors on 90 nm).
A discrete GPU (dGPU) sits on a separate PCIe card with its own dedicated VRAM (e.g. NVIDIA A100, 80 GB HBM2e), while an integrated GPU (iGPU) shares the die and main memory with the CPU (e.g. Apple Silicon). iGPUs are power-efficient but lack the memory bandwidth and compute density of discrete cards. ML frameworks (PyTorch, JAX) abstract the hardware behind device backend APIs, e.g. torch.device(“cuda | mps | cpu”), dispatching kernels and memory allocation to the appropriate driver. CUDA’s mature toolchain (cuDNN, cuBLAS, NCCL, TensorRT) gives NVIDIA a dominant position in production ML.
Compute unified device architecture (CUDA), released in 2006, made GPUs programmable for general-purpose computing by providing a C-based programming model that abstracted away graphics concepts. That is, general-purpose GPU required abusing graphics APIs which expressed computations as texture operations in OpenGL shading language (GLSL) or C for graphics (Cg). Matrix operations exhibit data parallelism that maps naturally onto GPU hardware, and CUDA’s ecosystem would later prove decisive for the deep learning revolution.
tmp. –
GPU compute performance is measured in floating-point operations per second (FLOPS). Peak FLOPS depends on core count, clock speed, and precision format, with lower precision yielding higher throughput since more operations fit per cycle. For instance, A100 delivers 19.5 TFLOPS at FP32 and 312 TFLOPS at FP16 via tensor cores, while achieved FLOPS falls well below peak in practice due to memory stalls, warp divergence, low occupancy, and synchronisation overhead. Subsequently, Model FLOPS utilisation (MFU), which is the ratio of achieved to theoretical peak, is the standard efficiency metric for training runs where 30-60% is typical at scale.
Notice that FLOPS and FLOPs, both looking like a twin, measure different things. FLOPS is a rate, counting floating-point operations completed per second, used in peak-performance claims as in “312 TFLOPS on the A100”. FLoating point OPerations (FLOPs) is a count measuring the total operations in a workload, used in ratios like arithmetic intensity (FLOPs/byte) or training budgets ($\sim 3.14 \times 10^{23}$ FLOPs for GPT-3). The two relate via “wallclock time” $\approx$ “total FLOPs” $/$ “effective FLOPS”, so the same workload runs faster either by reducing its FLOPs (e.g. quantisation, sparsity) or by raising delivered FLOPS (e.g. better kernels, higher MFU).
- …
2.2. Streaming Multiprocessor
A GPU consists of streaming multiprocessors (SMs), each a self-contained processing unit with its own register file, shared mem./L1 cache, warp schedulers, and execution units. The fundamental scheduling unit is a warp, a bundle of 32 threads executing the same instruction in lockstep, following the SIMT model. If threads within a warp take different branches (warp divergence), the SM executes each path separately and wastes slots for inactive threads. Each SM runs multiple warp schedulers that switch to a ready warp the moment one stalls on a memory access, hiding memory latency through parallelism (i.e. more threads) rather than large caches.
A CUDA core is a scalar execution unit within an SM that performs one floating-point or integer operation per cycle per thread. They suit graphics and general parallel workloads but lack hardware for the dense matrix multiply-accumulates (MMAs), which together with non-linear activation functions are the heart of deep learning. Tensor cores, introduced with Volta in the Tesla V100 (2017), address this by performing a 4×4×4 mixed-precision matrix multiply-accumulate (FP16 × FP16 → FP32), i.e. 64 multiply-adds per clock. These 640 tensor cores equipped in V100 can deliver 125 TFLOPS mixed-precision, an order of magnitude beyond its 15.7 TFLOPS FP32 CUDA throughput.
Micikevicius et al. (2017) showed that most forward and backward computations tolerate FP16 precision, with only weight updates requiring FP32 accuracy. Two techniques make this work: (i) maintaining an FP32 master copy of weights, and (ii) loss scaling to preserve small gradients in FP16’s limited range. PyTorch 1.6+ supports automatic mixed precision (AMP) via @torch.autocast and torch.GradScaler, making this technique accessible with minimal code changes. Subsequent generations expanded tensor core support: the Ampere architecture in the A100 (2020) added TF32, BF16, INT8, and structured sparsity (2:4 pattern, up to 2× speedup).
The Hopper architecture in the H100 (2022) introduced the Transformer Engine, hardware-software logic that dynamically chooses between FP8 and FP16/BF16 precision per layer during training, maximising throughput while preserving accuracy. The Blackwell architecture in the B200 (2024) also added 5th-generation tensor cores with a 2nd-generation Transformer Engine. That is, GPU hardware co-evolved as transformer models scaled. Tensor cores shifted to transformer-optimised primitives, adding native support for attention, lower-precision formats (FP8, FP4), and on-chip precision management to match the compute patterns of modern LLMs.
2.3. GPU Memory
GPU memory forms a deep hierarchy: each SM has its own register file (~256 KB on Volta) and shared memory / L1 SRAM (48-164 KB), all SMs share an L2 cache (40 MB on the A100), and off-chip global memory (HBM/GDDR) holds the most capacity at 200-600 cycle latency. Software has evolved to mask transfers at each boundary of this hierarchy. DMA allows the GPU to read/write host memory without CPU involvement, but the DMA engine operates on physical addresses and cannot safely access pageable host memory since the OS may swap it out. The CUDA driver therefore stages data via a page-locked (pinned) buffer before initiating DMA.
High-bandwidth memory (HBM), commonly referred to as VRAM, achieves its performance through vertical stacking. Multiple DRAM dies interconnected by through-silicon vias (TSVs) and mounted on a silicon interposer next to the GPU die. Each stack exposes a 1,024-bit interface at moderate clock speeds, opposite to GDDR’s narrow buses at high clock speeds. Successive generations from SK Hynix and Samsung (HBM2 → HBM2e → HBM3 → HBM3e) pushed per-stack bandwidth from ~250 GB/s to ~1.2 TB/s, while the H100’s five HBM3 stacks deliver 3.35 TB/s for 80 GB, and the H200 (HBM3e) reaches ~4.8 TB/s. Wider bus and lower power per bit make HBM the AI/HPC standard.
The roofline model in S Williams (2009) formalises GPU performance by placing a kernel under either the flat ceiling of peak FLOPS (compute-bound) or the sloped bandwidth ceiling (memory-bound), determined by its arithmetic intensity (FLOPs/byte moved from memory). Modern GPUs widen the gap, with an H100 needing ~300 FLOPs/byte to saturate BF16 tensor cores, far above what most kernels reach. Attention $\mathrm{softmax}(QK^\top)V$, embeddings $E[i]$, and activations $\sigma(x_i)$ are memory-bound, while matmuls $C = AB$ are compute-bound. FlashAttention tiles $Q$/$K$/$V$ into shared memory blocks so that the $N \times N$ score matrix never materialises in HBM, converting a memory-bound operation into a compute-bound one.
2.4. Interconnect
PCIe is the standard system interconnect, with each generation (Gen) roughly doubling bandwidth, from Gen 1 (2003, ~4 GB/s ×16), Gen 2 (2007, ~8 GB/s), Gen 3 (2010, ~16 GB/s), Gen 4 (2017, ~32 GB/s), Gen 5 (2019, ~64 GB/s), to Gen 6 (2022, ~128 GB/s via PAM-4 modulation). A PCIe link aggregates full-duplex serial lanes (×1, ×4, ×8, ×16) with differential signalling, replacing the shared parallel PCI and AGP buses of the early 2000s. Yet for multi-GPU HPC and AI workloads this is insufficient, as training large models exchanges gradients, activations, and KV caches at rates that saturate even Gen 5.
NVLink (Pascal) is a GPU-to-GPU interconnect doubling per generation, from 1.0 (160 GB/s, P100), 2.0 (300 GB/s, V100), 3.0 (600 GB/s, A100), 4.0 (900 GB/s, H100), to 5.0 (1.8 TB/s, B200). NVSwitch (Volta) is a switch chip giving all-to-all GPU links at full NVLink bandwidth, past point-to-point topology limits. DGX systems pack these into multi-GPU servers (e.g. DGX H100 with 8× H100), while the GB200 NVL72 wires 72 Blackwell GPUs at 1.8 TB/s into a rack-scale node. The nvidia collective communications library (NCCL) implements topology-aware all-reduce, all-gather, and broadcast primitives, the backbone of distributed training in PyTorch and JAX.
For cluster-level communication, InfiniBand, a switched fabric for HPC and AI clusters, provides remote DMA (RDMA), enabling zero-copy, kernel-bypass transfer at sub-$\mu\mathrm{s}$ latency. Bandwidth has grown from SDR (10 Gbps) to NDR (400 Gbps), with XDR (1.6 Tbps) planned, and NVIDIA acquired Mellanox in 2020 for $6.9 billion. The emerging Compute Express Link (CXL) standard, on the PCIe physical layer, adds cache-coherent protocols (CXL.io, CXL.cache, CXL.mem) letting CPUs and accelerators share coherent memory, unlocking heterogeneous compute and memory pooling.
–>
(C:)
I gathered words solely for my own purposes without any intention to break the rigour of the subjects.
I also prefer eating corn in spiral .