PICO-8 Wiki
m (Felice Enellen moved page CPU Source to CPU Benchmark Program: I saw you say you weren't sure about the name of this page, and I agree. I think this is more descriptive and less ambiguous to the reader)
(no need to justify the title anymore :))
Line 1: Line 1:
This internal page contains the "source" for the [[CPU]] page - a profiler that outputs cycle counts that were used in creating the data in the CPU page (from version 0.2.0 and on), to allow more easily adding & updating information on that page.
+
This internal page contains the source code for the benchmark program used to time operations on the [[CPU]] page - a profiler that outputs cycle counts that were used in creating the data in the CPU page (from version 0.2.0 and on), to allow more easily adding & updating information on that page.
   
 
It also contains (further below) the results of running this profiler on the last version the CPU page was updated for, to allow more easily determining whether there were any changes in the cycle counts.
 
It also contains (further below) the results of running this profiler on the last version the CPU page was updated for, to allow more easily determining whether there were any changes in the cycle counts.

Revision as of 08:34, 8 May 2020

This internal page contains the source code for the benchmark program used to time operations on the CPU page - a profiler that outputs cycle counts that were used in creating the data in the CPU page (from version 0.2.0 and on), to allow more easily adding & updating information on that page.

It also contains (further below) the results of running this profiler on the last version the CPU page was updated for, to allow more easily determining whether there were any changes in the cycle counts.

You are encouraged to update this page whenever you add to or update the CPU page.

(But note that there is no blind 1:1 correspondence between the output of this program and the information in the CPU page - thinking is required to interpret the results correctly.)

[By the by, it can be enlightening to know that the cycle count is (most likely) not really affected by the pico8 lua source, but by the lua VM bytecode that the source gets compiled into]

Profiler code

function testme_calib(name, func, calibrate_func, ...)
    -- based on https://www.lexaloffle.com/bbs/?pid=60198#p
    local n = 1024
    
    -- calibrate
    flip()
    local unused -- I am not sure why this helps give better results, but it does, so.
    
    local x,t=stat(1),stat(2)
    for i=1,n do
      calibrate_func(...)
    end
    local y,u=stat(1),stat(2)
    
    -- measure
    for i=1,n do
      func(...)
    end
    local z,v=stat(1),stat(2)
    
    -- report
    local function c(t0,t1,t2) return(t0+t2-2*t1)*128/n*256/60*256*2 end -- *2 for 0.2.x
    
    local s=name.." :"
    local lc=c(x-t,y-u,z-v)
    if (lc != 0) s..=" lua="..lc
    local sc=c(t,u,v)
    if (sc != 0) s..=" sys="..sc
    
    print(s) -- no paging, so not very useful, but.
    printh(s)
end

function testme(name, func, ...)
    return testme_calib(name, func, function() end, ...)
end

-- these guys aren't strictly necessary since args are equivalent to locals, but...

function testme_local_1(name, func, ...)
    return testme_calib(name, func, function() local x=1 end, ...)
end

function testme_local_1_2(name, func, ...)
    return testme_calib(name, func, function() local x,y=1,2 end, ...)
end

-- operators
testme("+", function(x, y) return x + y end, 1, 2)
testme("-", function(x, y) return x - y end, 1, 2)
testme("*", function(x, y) return x * y end, 2, 3)
testme("/", function(x, y) return x / y end, 2, 3)
testme("\\", function(x, y) return x \ y end, 2, 3)
testme("%", function(x, y) return x % y end, 2, 3)
testme("&", function(x, y) return x & y end, 2, 3)
testme("|", function(x, y) return x | y end, 2, 3)
testme("^^", function(x, y) return x ^^ y end, 2, 3)
testme("<<", function(x, y) return x << y end, 2, 3)
testme(">>", function(x, y) return x >> y end, 2, 3)
testme(">>>", function(x, y) return x >>> y end, 2, 3)
testme("<<>", function(x, y) return x <<> y end, 2, 3)
testme(">><", function(x, y) return x >>< y end, 2, 3)

testme("unary -", function(x) return -x end, 1)
testme("unary ~", function(x) return ~x end, 1)
testme("unary not", function(x) return not x end, 1)

testme("@", function(x) return @x end, 0)
testme("%", function(x) return %x end, 0)
testme("$", function(x) return $x end, 0) 

testme("..", function(x, y) return x .. y end, "12341231231", "6675612312")

testme("2^3", function(x, y) return x ^ y end, 2, 3)
testme("100^2.5", function(x, y) return x ^ y end, 100, 2.5)
testme("2^14.5", function(x, y) return x ^ y end, 2, 14.5)
 
testme("<", function(x, y) return x < y end, 1, 5)
testme(">", function(x, y) return x > y end, 1, 5)
testme("<=", function(x, y) return x <= y end, 1, 5)
testme(">=", function(x, y) return x >= y end, 1, 5)
testme("==", function(x, y) return x == y end, 1, 5)
testme("!=", function(x, y) return x != y end, 1, 5)

testme("and:t", function(x, y) return x and y end, 1, 2)
testme("and:f", function(x, y) return x and y end, false, 2)
testme("and+", function(x, y) return (x and y)+1 end, 1, 2)
testme("andand", function(x, y) return (x and y) and y end, 1, 2)
testme("or:t", function(x, y) return x or y end, 1, 2)
testme("or:f", function(x, y) return x or y end, false, 2)
testme("oror", function(x, y) return (x or y) or y end, false, 2)

testme("==and", function(x, y, z, w) return x == y and z end, 1, 1, 2, 2)
testme("and==", function(x, y, z, w) return z and x == y end, 1, 1, 2, 2)
testme("==and==", function(x, y, z, w) return x == y and z == w end, 1, 1, 2, 2)
testme("==and==and==", function(x, y, z, w) return x == y and z == w and x == y end, 1, 1, 2, 2)
testme("====", function(x, y, z) return x == y == z end, 1, 1, 1)

-- statements
g_x = 1
local up_x, up_y = 1, 1

testme("return", function(x) return end, 1) -- if this is 0, this merely means cost of "return" is equivalent to the cost of implicit return, which may yet be non-0 (compare gcall vs primcall for cost)
testme("return 1", function(x) return 1 end, 1) 
testme("return 1,2", function(x) return x,x end, 1) 
testme("return 1,2,3", function(x) return x,x,x end, 1) 
testme(";", function() ;;; end)
testme("local", function() local x end)
testme("local=init", function() local x=1 end)
testme("local*4", function() local x,y,z,w end)
testme("local*4=init*1", function() local x,y,z,w=1 end)
testme("local*4=init*4", function() local x,y,z,w=1,2,3,4 end)
testme("return arg", function(x) return x end, 1)
testme("return arg + arg", function(x) return x + x end, 1)
testme_local_1("return local", function() local x=1; return x end)
testme_local_1("return local + local", function() local x=1; return x + x end)
testme("return global", function() return g_x end)
testme("return global(new)", function() return g_x_NEW end)
testme("return global + global", function() return g_x + g_x end)
testme("return upvalue", function() return up_x end)
testme("return upvalue + upvalue", function() return up_x + up_x end)
testme("arg=", function(x) x = 1 end, 1)
testme_local_1("local=", function() local x=1; x = 1 end)
testme("global=", function() g_x = 1 end)
testme("global=global + global", function() g_x = g_x + g_x end)
testme("upvalue=", function() up_x = 1 end)
testme("upvalue=upvalue + upvalue", function() up_x = up_x + up_x end)
testme_local_1("x=x+y", function(y) local x=1; x = x + y end, 3)
testme_local_1("x+=y", function(y) local x=1; x += y end, 3)
testme_local_1("x=y", function() local x=1; x = y end)
testme("arg x,y=y,x", function(x,y) x,y = y,x end)
testme_local_1_2("x,y=y,x", function() local x,y=1,2; x,y = y,x end)
testme_local_1_2("x,y=1,2", function() local x,y=1,2; x,y = 1,2 end)
testme_local_1_2("x,x=1,2", function() local x,y=1,2; x,x = 1,2 end)
testme_local_1_2("x,x,x=1,2,3", function() local x,y=1,2; x,x,x = 1,2,3 end)
testme_local_1_2("x,x,x=1+,2+,3+", function() local x,y=1,2; x,x,x = 1+y,2+y,3+y end)
testme_local_1_2("x,x,x=1", function() local x,y=1,2; x,x,x = 1 end)
testme_local_1_2("x,x,x=1,2,3,4", function() local x,y=1,2; x,x,x = 1,2,3,4 end)
testme_local_1_2("x,x,x=1,2,3,4+", function() local x,y=1,2; x,x,x = 1,2,3,4+y end)
testme_local_1_2("x,x,x=1+,2+,3+,4+", function() local x,y=1,2; x,x,x = 1+y,2+y,3+y,4+y end)

-- tables
testme("[i]", function(x) return x[1] end, {1,2,3})
testme("[i](NEW)", function(x) return x[1] end, {})
testme("[k]", function(x) return x['a'] end, {a=1})
testme("[k](NEW)", function(x) return x['a'] end, {})
testme("[i]=", function(x) x[1]=1 end, {2,3,4})
testme("[i](NEW)=", function(x) x[1]=1 end, {})
testme("[k]=", function(x) x['a']=1 end, {a=2})
testme("[k](NEW)=", function(x) x['a']=1 end, {})

testme("{}", function(x) return {} end)
testme("{1}", function(x) return {1} end)
testme("{1,2}", function(x) return {1,2} end)
testme("{1..3}", function(x) return {1,2,3} end)
testme("{1..6}", function(x) return {1,2,3,4,5,6} end)
testme("{a}", function(x) return {a=1} end)
testme("{a,b}", function(x) return {a=1,b=2} end)
testme("{a..c}", function(x) return {a=1,b=2,c=3} end)
testme("{a..e}", function(x) return {a=1,b=2,c=3,d=4,e=5} end)
testme("{a..e,1}", function(x) return {a=1,b=2,c=3,d=4,e=5,1} end)
testme("{a..e,1..4}", function(x) return {a=1,b=2,c=3,d=4,e=5,1,2,3,4} end)

testme("{1+2}", function(x) return {1+2} end)

testme("#", function(x) return #x end, {1,2,3})

-- functions
gcall = function() end
local upcall = function() end

testme("func", function() return function() end end)
testme("func<up>", function(x) return function() return x end end)
testme("func<upup>", function(x) return function() return g_up end end)
testme("func<_env>", function(x) return function() return sin end end)

testme("call()", function(f) f() end, function() end)
testme("call(1)", function(f) f(1) end, function() end)
testme("call(1,2)", function(f) f(1,2) end, function() end)
testme("call(1..5)", function(f) f(1,2,3,4,5) end, function() end)
testme("gcall()", function() gcall() end)
testme("gcall(1)", function() gcall(1) end)
testme("gcall(1,2)", function() gcall(1,2) end)
testme("gcall(1..5)", function() gcall(1,2,3,4,5) end)
testme("upcall()", function() upcall() end)
testme("upcall(1)", function() upcall(1) end)
testme("upcall(1,2)", function() upcall(1,2) end)
testme("upcall(1..5)", function() upcall(1,2,3,4,5) end)
testme("primcall()", function() min() end)
testme("primcall(1)", function() min(1) end)
testme("primcall(1,2)", function() min(1,2) end)
testme("primcall(1..5)", function() min(1,2,3,4,5) end)

testme("call(1+2)", function(f) f(1+2) end, function() end)
testme("call(1==2)", function(f) f(1==2) end, function() end)
testme("call(call())", function(f) f(f()) end, function() end)

-- complex statements
testme("if:t", function(x) if x then end end, true)
testme("if:f", function(x) if x then end end, false)
testme("if==", function(x) if x==x then end end, false)
testme("if!=", function(x) if x!=x then end end, false)
testme("ifel", function(x) if x then elseif x then end end, false)
testme("ifelelse", function(x) if x then elseif x then else end end, false)
testme("while:f", function(x) while x do end end, false)
testme("while<=:f", function(x) while x<=0 do end end, 1)
testme_local_1("while<0>,+=", function() local x=1; while x<=0 do x+=1 end end) -- don't forget to discount the x+=1
testme_local_1("while<1>,+=", function() local x=1; while x<=1 do x+=1 end end)
testme_local_1("while<2>,+=", function() local x=1; while x<=2 do x+=1 end end)
testme_local_1("while<5>,+=", function() local x=1; while x<=5 do x+=1 end end)
testme_local_1("while<10>,+=", function() local x=1; while x<=10 do x+=1 end end)
testme("for<n:0>", function() for i=1,0 do end end)
testme("for<n:1>", function() for i=1,1 do end end)
testme("for<n:2>", function() for i=1,2 do end end)
testme("for<n:5>", function() for i=1,5 do end end)
testme("for<n:10>", function() for i=1,10 do end end)
-- TODO: non-numeric for!
testme("do", function() do end end)

-- misc
local meta = {__add=function() end}
setmetatable(meta, meta)
testme("+meta", function(x) return x+x end, meta)

-- funcs
testme("peek", function() peek(0) end)
testme("peek2", function() peek2(0) end)
testme("peek4", function() peek4(0) end)
testme("poke", function() poke(0,0) end)
testme("poke2", function() poke2(0,0) end)
testme("poke4", function() poke4(0,0) end)
testme("band", function() band(0,0) end)
testme("bor", function() bor(0,0) end)
testme("bxor", function() bxor(0,0) end)
testme("bnot", function() bnot(0) end)
testme("shl", function() shl(0,0) end)
testme("shr", function() shr(0,0) end)
testme("lshr", function() lshr(0,0) end)
testme("rotl", function() rotl(0,0) end)
testme("rotr", function() rotr(0,0) end)
testme("flr", function() flr(0) end)
testme("ceil", function() ceil(0) end)

testme("band_#0", function() band() end)
testme("band_#1", function() band(0) end)
testme("band_#3", function() band(0,0,0) end)

testme("min", function() min(0,0) end)
--testme("cls", function() cls() end)
-- TODO...

Results:

(On v0.2.0h)

+ : lua=1
- : lua=1
* : lua=2
/ : lua=2
\ : lua=2
% : lua=2
& : lua=1
| : lua=1
^^ : lua=1
<< : lua=1
>> : lua=1
>>> : lua=1
<<> : lua=1
>>< : lua=1
unary - : lua=2
unary ~ : lua=1
unary not : lua=2
@ : lua=1
% : lua=1
$ : lua=1
.. : lua=6
2^3 : lua=2 sys=16
100^2.5 : lua=2 sys=32
2^14.5 : lua=2 sys=32
< : lua=4
> : lua=4
<= : lua=4
>= : lua=4
== : lua=4
!= : lua=4
and:t : lua=4
and:f : lua=2
and+ : lua=5
andand : lua=6
or:t : lua=2
or:f : lua=4
oror : lua=4
==and : lua=6
and== : lua=6
==and== : lua=6
==and==and== : lua=8
==== : lua=8
return :
return 1 : lua=2
return 1,2 : lua=4
return 1,2,3 : lua=6
; :
local : lua=2
local=init : lua=2
local*4 : lua=2
local*4=init*1 : lua=4
local*4=init*4 : lua=8
return arg :
return arg + arg : lua=1
return local :
return local + local : lua=1
return global : lua=2
return global(new) : lua=2
return global + global : lua=5
return upvalue : lua=2
return upvalue + upvalue : lua=5
arg= : lua=2
local= : lua=2
global= : lua=2
global=global + global : lua=7
upvalue= : lua=4
upvalue=upvalue + upvalue : lua=7
x=x+y : lua=1
x+=y : lua=1
x=y : lua=2
arg x,y=y,x : lua=6
x,y=y,x : lua=6
x,y=1,2 : lua=6
x,x=1,2 : lua=6
x,x,x=1,2,3 : lua=10
x,x,x=1+,2+,3+ : lua=7
x,x,x=1 : lua=10
x,x,x=1,2,3,4 : lua=14
x,x,x=1,2,3,4+ : lua=13
x,x,x=1+,2+,3+,4+ : lua=10
[i] : lua=2
[i](NEW) : lua=2
[k] : lua=2
[k](NEW) : lua=2
[i]= : lua=2
[i](NEW)= : lua=2
[k]= : lua=2
[k](NEW)= : lua=2
{} : lua=2
{1} : lua=6
{1,2} : lua=8
{1..3} : lua=10
{1..6} : lua=16
{a} : lua=4
{a,b} : lua=6
{a..c} : lua=8
{a..e} : lua=12
{a..e,1} : lua=16
{a..e,1..4} : lua=22
{1+2} : lua=6
# : lua=2
func : lua=2
func<up> : lua=2
func<upup> : lua=2
func<_env> : lua=2
call() : lua=6
call(1) : lua=8
call(1,2) : lua=10
call(1..5) : lua=16
gcall() : lua=6
gcall(1) : lua=8
gcall(1,2) : lua=10
gcall(1..5) : lua=16
upcall() : lua=6
upcall(1) : lua=8
upcall(1,2) : lua=10
upcall(1..5) : lua=16
primcall() : lua=4
primcall(1) : lua=6
primcall(1,2) : lua=8
primcall(1..5) : lua=14
call(1+2) : lua=8
call(1==2) : lua=10
call(call()) : lua=12
if:t : lua=2
if:f : lua=2
if== : lua=2
if!= : lua=2
ifel : lua=4
ifelelse : lua=4
while:f : lua=2
while<=:f : lua=2
while<0>,+= : lua=2
while<1>,+= : lua=7
while<2>,+= : lua=12
while<5>,+= : lua=27
while<10>,+= : lua=52
for<n:0> : lua=10
for<n:1> : lua=12
for<n:2> : lua=14
for<n:5> : lua=20
for<n:10> : lua=30
do :
+meta : lua=3
peek : lua=4
peek2 : lua=4
peek4 : lua=4
poke : lua=4
poke2 : lua=4
poke4 : lua=4
band : lua=4
bor : lua=4
bxor : lua=4
bnot : lua=4
shl : lua=4
shr : lua=4
lshr : lua=4
rotl : lua=4
rotr : lua=4
flr : lua=4
ceil : lua=4
band_#0 : lua=4
band_#1 : lua=6
band_#3 : lua=10
min : lua=8