CPU: Difference between revisions
imported>Sam Hocevar No edit summary |
imported>Sam Hocevar (Add negative costs.) |
||
| Line 1: | Line 1: | ||
PICO-8 keeps track of CPU usage using two values: Lua cycles and system cycles. Most operations affect Lua cycles, but some functions have an additional system cycle cost. | |||
There are 4194304 cycles in a second (2^22), so about 69905 cycles per frame at 60 FPS, or 139810 cycles at 30 FPS. The function [[stat|<code>stat(1)</code>]] returns the total (Lua + system) cycle ratio for the current frame, and [[stat|<code>stat(2)</code>]] returns the system ratio.<blockquote>Example: since [[cls|<code>cls()</code>]] uses 2 Lua cycles and 1024 system cycles, PICO-8 running at 60 FPS can call [[cls|<code>cls()</code>]] about <code>2^22/60/1026 = 68</code> times.</blockquote> | There are 4194304 cycles in a second (2^22), so about 69905 cycles per frame at 60 FPS, or 139810 cycles at 30 FPS. The function call [[stat|<code>stat(1)</code>]] returns the total (Lua + system) cycle ratio for the current frame, and [[stat|<code>stat(2)</code>]] returns the system ratio.<blockquote>Example: since [[cls|<code>cls()</code>]] uses 2 Lua cycles and 1024 system cycles, PICO-8 running at 60 FPS can call [[cls|<code>cls()</code>]] about <code>2^22/60/1026 = 68</code> times.</blockquote> | ||
== Lua cycles == | == Lua cycles == | ||
Some standard Lua operation costs: | Some standard Lua operation costs: | ||
* | * Assignations: 1 cycle per assignation | ||
* Arithmetic operation: 1 cycle (note that <code>x^y</code> also has a variable system cost) | * Arithmetic operation: 1 cycle (note that <code>x^y</code> also has a variable system cost) | ||
* Function | * Boolean operations: 2 cycles if second argument is evaluated, 1 cycle if short circuit | ||
* | * String concatenation: 3 cycles | ||
* Table element access: 1 cycle | |||
* Function call: 2+n cycles, where n is the number of arguments | |||
* '''do … end''': 0 cycles | |||
* Loop: 1 cycle per iteration | |||
Lua CPU stats are only updated every 1024 cycles. | Lua CPU stats are only updated every 1024 cycles. | ||
== Functions that add negative Lua cycles == | |||
Some functions have negative Lua cycles associated with them that get subtracted from the Lua cycle count by the PICO-8 runtime. This mechanism allows PICO-8 to make these functions artificially cheaper. | |||
For instance, [[shl|<code>shl(x,y)</code>]] should cost 4 cycles because it is a function call with two arguments, but each call subtracts 3 cycles from the Lua cycle counter, for a total of 1 cycle. | |||
The table below lists functions that have their total cost tweaked in this way. | |||
{| | |||
| Function || Adjusted cycles || Notes | |||
|- | |||
| [[peek|<code>peek(x)</code>]] || <code>1</code> || only when called with 1 argument | |||
|- | |||
| [[poke|<code>poke(x,y)</code>]] || <code>1</code> || only when called with 2 arguments | |||
|- | |||
| [[peek4|<code>peek4(x)</code>]] || <code>n</code> || where <code>n</code> is the number of arguments | |||
([[peek4|<code>peek4()</code>]] costs zero cycles when no argument is given) | |||
|- | |||
| [[poke4|<code>poke4(x,y)</code>]] || <code>2</code> || only when called with 2 arguments | |||
|- | |||
| [[band|<code>band(x,y)</code>]] || <code>1</code> || only when called with 2 arguments | |||
|- | |||
| [[bor|<code>bor(x,y)</code>]] || <code>1</code> || only when called with 2 arguments | |||
|- | |||
| [[bxor|<code>bxor(x,y)</code>]] || <code>1</code> || only when called with 2 arguments | |||
|- | |||
| [[bnot|<code>bnot(x)</code>]] || <code>1</code> || only when called with 1 argument | |||
|- | |||
| [[shl|<code>shl(x,y)</code>]] || <code>1</code> || only when called with 2 arguments | |||
|- | |||
| [[shr|<code>shr(x,y)</code>]] || <code>1</code> || only when called with 2 arguments | |||
|- | |||
| [[lshr|<code>lshr(x,y)</code>]] || <code>1</code> || only when called with 2 arguments | |||
|- | |||
| [[rotl|<code>rotl(x,y)</code>]] || <code>1</code> || only when called with 2 arguments | |||
|- | |||
| [[rotr|<code>rotr(x,y)</code>]] || <code>1</code> || only when called with 2 arguments | |||
|- | |||
| [[flr|<code>flr(x)</code>]] || <code>1</code> || only when called with 1 argument | |||
|- | |||
| [[ceil|<code>ceil(x)</code>]] || <code>1</code> || only when called with 1 argument | |||
|} | |||
Notable math functions that do not have negative cycles are: [[sgn|<code>sgn(x)</code>]], [[abs|<code>abs(x)</code>]], the comparison functions, and the trigonometric functions. | |||
== System cycles == | == System cycles == | ||
Revision as of 23:28, 20 December 2018
PICO-8 keeps track of CPU usage using two values: Lua cycles and system cycles. Most operations affect Lua cycles, but some functions have an additional system cycle cost.
There are 4194304 cycles in a second (2^22), so about 69905 cycles per frame at 60 FPS, or 139810 cycles at 30 FPS. The function call stat(1) returns the total (Lua + system) cycle ratio for the current frame, and stat(2) returns the system ratio.
Example: since
cls()uses 2 Lua cycles and 1024 system cycles, PICO-8 running at 60 FPS can callcls()about2^22/60/1026 = 68times.
Lua cycles
Some standard Lua operation costs:
- Assignations: 1 cycle per assignation
- Arithmetic operation: 1 cycle (note that
x^yalso has a variable system cost) - Boolean operations: 2 cycles if second argument is evaluated, 1 cycle if short circuit
- String concatenation: 3 cycles
- Table element access: 1 cycle
- Function call: 2+n cycles, where n is the number of arguments
- do … end: 0 cycles
- Loop: 1 cycle per iteration
Lua CPU stats are only updated every 1024 cycles.
Functions that add negative Lua cycles
Some functions have negative Lua cycles associated with them that get subtracted from the Lua cycle count by the PICO-8 runtime. This mechanism allows PICO-8 to make these functions artificially cheaper.
For instance, shl(x,y) should cost 4 cycles because it is a function call with two arguments, but each call subtracts 3 cycles from the Lua cycle counter, for a total of 1 cycle.
The table below lists functions that have their total cost tweaked in this way.
| Function | Adjusted cycles | Notes |
peek(x) |
1 |
only when called with 1 argument |
poke(x,y) |
1 |
only when called with 2 arguments |
peek4(x) |
n |
where n is the number of arguments
( |
poke4(x,y) |
2 |
only when called with 2 arguments |
band(x,y) |
1 |
only when called with 2 arguments |
bor(x,y) |
1 |
only when called with 2 arguments |
bxor(x,y) |
1 |
only when called with 2 arguments |
bnot(x) |
1 |
only when called with 1 argument |
shl(x,y) |
1 |
only when called with 2 arguments |
shr(x,y) |
1 |
only when called with 2 arguments |
lshr(x,y) |
1 |
only when called with 2 arguments |
rotl(x,y) |
1 |
only when called with 2 arguments |
rotr(x,y) |
1 |
only when called with 2 arguments |
flr(x) |
1 |
only when called with 1 argument |
ceil(x) |
1 |
only when called with 1 argument |
Notable math functions that do not have negative cycles are: sgn(x), abs(x), the comparison functions, and the trigonometric functions.
System cycles
Only a few functions consume system cycles. Note that they will add to their standard Lua cycle cost.
System CPU stats are updated after each call.
Here is the list, measured on PICO-8 1.1.11g:
| Function | Cycles | Notes |
cls() |
1024 |
same cost as rectfill of same size
|
print() |
2+n*8
|
n is the number of characters in the string, even those not rendered
spaces, newlines, and double-width glyphs each count as one character |
spr() |
n | n is the number of pixels drawn, including transparent pixels (width × height of the sprite rectangle)
cost is 0 if first argument is outside the [0, 255] range |
sspr() |
n
|
n is the number of pixels drawn, including transparent pixels (width × height of the destination rectangle)
|
rect() |
max(1,2*ceil(a/4)) + max(0,2*ceil(b/2-1))
|
Where:
|
rectfill() |
max(1,flr(n/16)) |
n is the number of pixels drawn (width × height)
|
circ() |
2+n*4
|
warning: that formula is incomplete for clipped circles |
circfill() |
n*flr((n+9)/4)
|
warning: that formula is incomplete for clipped circles |
line() |
ceil(n/2) |
n is the number of pixels drawn; there is an additional cost of 1 if at least one pixel had to be clipped
|
map() / mapdraw() |
max(1,n*64)
|
n is the number of sprites rendered; only cells that are not zero in the map are considered
|
music() |
16 |
no cost if no argument |
sfx() |
16 |
no cost if no argument |
memcpy() |
n+1
|
n is the number of bytes copied
|
memset() |
max(1,ceil(n/2))
|
n is the number of bytes set
|
reload() |
max(1,n*8)
|
n is the number of bytes reloaded
|
btn() |
4 |
no cost if no argument |
btnp() |
4 |
no cost if no argument |
rnd() |
4 |
|
sqrt() |
24 |
only 16 if argument is zero
|
x^y |
8*(n+1) |
n is the position of the last fractional bit in y; for instance, cost is 8 for any integer such as y == 13, and is 8*3 for y == 1.25
|
stat() |
16 |