Create: Computing AgesBrass Docs
Programming the world

Speed, memory and limits

The instruction budget of each computer and how rotation sets it, what costs instructions, memory counted in cells, the hard limits, and how to write programs that stay fast.

Each computer is a machine with a speed and a memory, like the real ones of its age: the Tube Computer runs 400 instructions per second, the Modern Computer 100,000. This page explains how that speed is counted, what a program spends it on, how memory is measured, and the hard limits that hold on every computer. Knowing them lets you pick the right computer for a job, and write programs that stay quick on the slow ones.

The instruction budget

A program runs as a list of small instructions (push a value, read a variable, add two numbers, jump...). Each tick, a computer may run a fixed number of them, its budget. When the budget is spent, the program simply stops where it is and goes on at the next tick. Nothing breaks: a long computation is just spread over more ticks.

computerinstructions per tickper secondmemory (cells)
Tube Computer204002,048
Transistor Mainframe801,6008,192
Minicomputer3006,00032,768
Personal Computer1,20024,000131,072
Microcontroller4008,00016,384
Modern Computer5,000100,0001,048,576

These figures are reached at 256 RPM. The budget follows the rotation: it is multiplied by RPM / 256, so a computer at 128 RPM runs half as many instructions, at 32 RPM an eighth. The direction of rotation does not matter, and turning faster than 256 RPM gives nothing more. Engineer's Goggles show the instructions per second of a computer at its current speed.

Fractions are carried over: a Tube Computer at 16 RPM gets 1.25 instructions per tick, so it runs 1 instruction on three ticks out of four and 2 on the fourth. Even a very slow computer always moves forward.

Two more rules:

  • Unused budget is lost. When the program waits (sleep, os.pull_event, read), what is left of the tick's budget is not saved for later. Waiting is free, but it does not earn credit.
  • A debt is paid on the next ticks. Some built-in functions cost more than one instruction (see below). When one of them costs more than what is left, the budget goes below zero, and the next ticks pay the debt first. On a Transistor Mainframe, a gfx.clear() of the whole screen costs about 820 instructions: ten ticks of budget, so the program stands still for half a second after it.

A slower rotation slows the program, not time: sleep(1) still lasts 20 ticks, and timers ring on time (see Time and timers). Without rotation, the computer is frozen.

What costs instructions

Every step of the VM costs 1. A line of code is a handful of steps:

codeinstructions
an empty for turn3
for turn with total = total + i7
while turn with i = i + 1 and its test9
calling a small function of your ownabout 5 more than writing its body in place
print("hello")5

Calling a built-in function costs 1, plus extra for those that do work in proportion to their data:

functionextra cost
print, write, term.write1 per 16 characters written
string.find, string.split1 per 16 characters of the text
string.rep1 per 8 repetitions
table.concat1 per 16 characters of the result
table.insert and table.remove at a positionabout 1 per 8 elements of the list
table.contains1 per 8 elements
table.sortabout n × log2(n) for n elements (with your own comparison function, its code is counted instead)
fs.read, fs.write, fs.append, fs.copy1 per 16 characters
import1 per 16 characters of the file
gfx drawing1 per 64 pixels drawn
gfx.text1 per character, times the scale squared
peripheral.list, peripheral.find16, plus 4 per block on the cable network
peripheral.wrap8
inventory list, count, push1 per slot
kinetic stress, capacity1 per block of the kinetic network
net.send32, plus the network walk and the size of the message
net.broadcast64, plus 8 per computer reached
vehicle.position, velocity, speed8
os.memory1 per 16 cells in use

The Peripherals section details the peripheral costs. Everything else (math, most string functions, reading a redstone face, setting an output...) costs 1.

Why the server never lags

A computer can never make the server slow, whatever its program does:

  • It runs at most its budget per tick. while true do end takes its own computer's whole budget forever, and nothing else: the other computers and the world go on as usual.
  • Built-in functions that do a lot of work charge for it, as in the tables above, so drawing, copying text or scanning a network cannot be done for free.
  • Changes to the world (redstone outputs, Redstone Links, speeds of Rotation Speed Controllers) are applied once per tick, with the last value asked: a loop toggling an output a thousand times costs the world one update (see Redstone and Create links).
  • The screen travels to players at most every 2 ticks (10 times a second), and only the parts that changed.
  • The safety valve. Beyond a number of computers run in the same tick (512 by default), the next ones only run every other tick. Their programs and their clocks then go at half speed: a sleep(1) lasts two real seconds. Server owners set this number with maxComputersTickedPerTick in world/serverconfig/computingages-server.toml (the screen interval is terminalSyncInterval in the same file).

Memory

Memory is counted in cells. The table at the top gives each computer's total. What takes cells:

datacells
a table4, plus 2 per entry (key and value)
a string1 + its length / 8 (rounded down)
a number, true, false, nilnothing more than the place that holds it
a function value4
a variable of the file, a local, a value being computed1
a call in progress (a frame)8
a global variable2, plus its name
the code of a loaded fileabout 1 per 4 VM instructions, plus 1 per constant
an event waiting in the queue6, plus 2 per field, plus its values

Only data the program can still reach counts: a table that nothing points to any more is free again, without doing anything. os.memory() measures what is in use now and returns {used = ..., total = ...}; the shell command mem prints the same.

Brass
local before = os.memory().used
local levels = {}
for i = 1, 100 do
  levels[i] = i * 2
end
local middle = os.memory().used
local names = {}
for i = 1, 10 do
  names[i] = "pressed iron sheet #" .. i
end
local after = os.memory().used
print("100 numbers: " .. middle - before .. " cells")
print("10 names: " .. after - middle .. " cells")
Screen
100 numbers: 204 cells
10 names: 54 cells

The list of 100 numbers takes 4 cells for the table and 2 per entry. The 10 names take 4 for their table, 2 per entry, and 3 per string of 21 or 22 characters. A list of 1000 numbers (2004 cells) is nearly the whole memory of a Tube Computer, and nothing for a Personal Computer.

Measuring walks through everything the program holds, so os.memory() costs 1 instruction per 16 cells in use: call it now and then, not in a tight loop.

When the live data really exceed the total, the program stops with out of memory. The usual causes are a list or a log that grows forever (keep only the last 100 lines, or write them to a file), and text read whole from a big file. The events waiting in the queue count too: a computer that receives messages faster than it reads them refuses new ones once they fill a quarter of its memory (net.send then returns false on the sending side).

Hard limits

Whatever the computer, these limits hold:

limitvaluebeyond it
nested calls200stack overflow
length of a string65,536 charactersstring too long
events in the queue256the oldest is dropped
timers waiting256too many timers
Redstone Link frequencies32too many link frequencies (max 32)
files and folders per medium1,024too many files (1024 at most)
length of a path128 characterspath too long (128 characters at most)
a network message2,048 values, 32,768 charactersmessage too large
pasted text4,096 characterscut
label of a computer32 charactersbad argument #1 to 'label' (at most 32 characters)
reboots2 per seconddelayed

The Limits page lists them all.

Writing fast programs

Wait instead of checking. A program waiting in os.pull_event or sleep costs nothing. A loop that checks a face over and over burns its whole budget for the same result:

Brass
-- wasteful: thousands of instructions per second for nothing
while rs.get("left") == 0 do
end

-- free until the face changes
repeat
  os.pull_event("redstone")
until rs.get("left") > 0

Wrap peripherals once. peripheral.find scans the whole cable network each time: call it at the start and keep the table (see Peripherals).

Draw only what changed. A full term.clear() and redraw, or a gfx.clear(), every tick costs a lot on a big screen, and players only see 10 frames a second anyway. Rewrite the line whose value changed, redraw the screen on a timer a few times a second, and skip the redraw when nothing changed.

Keep library functions close in hot loops. Reading a global and reading a local cost the same, but math.floor(x) first reads math then floor: a local copy saves one instruction per call.

Brass
local floor = math.floor
for i = 1, 1000 do
  local stack = floor(i / 64)   -- one instruction less than math.floor
end

**Prefer for to while** for counting loops (7 instructions per turn instead of 9 in the measures above), and inline a tiny function in a loop that runs thousands of times.

Choose the computer. The same program can be too slow for a Tube Computer and instant on a Personal Computer. A dashboard redrawn every second, a sorter that scans a big vault, a game: give them a fast computer, and a fast shaft.

Example: measuring the cost of a loop

How many instructions does a loop really cost? Time it with os.clock, then turn ticks into instructions with the budget of the computer. Save this as measure and run it on a Personal Computer turning at 256 RPM:

measure
local IPT = 1200   -- instructions per tick: Personal Computer at 256 RPM
local N = 10000

local start = os.clock()
local total = 0
for i = 1, N do
  total = total + i
end
local ticks = math.round((os.clock() - start) * 20)
print(string.format("%d ticks, about %.1f instructions per turn", ticks, ticks * IPT / N))
Terminal
> measure
58 ticks, about 7.0 instructions per turn

The loop runs 10,000 turns of 7 instructions: 70,000 instructions, about 58 ticks of a Personal Computer, three seconds. A Tube Computer would need almost three minutes for the same loop, a Modern Computer 14 ticks. Change the body of the loop to compare two ways of writing the same thing, and use a large N: the measure is precise to a tick.