185 lines
5.0 KiB
Lua

--
-- PERFTEST.LUA Copyright (c) 2007-08, Asko Kauppi <akauppi@gmail.com>
--
-- Performance comparison of multithreaded Lua (= how much ballast does using
-- Lua Lanes introduce)
--
-- Usage:
-- [time] lua -lstrict perftest.lua [threads] [-plain|-single[=2..n]] [-time] [-prio[=-2..+2[,-2..+2]]]
--
-- threads: number of threads to launch (loops in 'plain' mode)
-- -plain: runs in nonthreaded mode, to get a comparison baseline
-- -single: runs using just a single CPU core (or 'n' cores if given)
-- -prio: sets odd numbered threads to higher/lower priority
--
-- History:
-- AKa 20-Jul-08: updated to Lanes 2008
-- AK 14-Apr-07: works on Win32
--
-- To do:
-- (none?)
--
-- On MSYS, stderr is buffered. In this test it matters.
-- Seems, even with this MSYS wants to buffer linewise, needing '\n'
-- before actual output.
--
local MSYS= os.getenv("OSTYPE")=="msys"
require "lanes"
local m= require "argtable"
local argtable= assert( m.argtable )
local N= 1000 -- threads/loops to use
local M= 1000 -- sieves from 1..M
local PLAIN= false -- single threaded (true) or using Lanes (false)
local SINGLE= false -- cores to use (false / 1..n)
local TIME= false -- use Lua for the timing
local PRIO_ODD, PRIO_EVEN -- -3..+3
local function HELP()
io.stderr:write( "Usage: lua perftest.lua [threads]\n" )
end
-- nil -> +2
-- odd_prio[,even_prio]
--
local function prio_param(v)
if v==true then return 2,-2 end
local a,b= string.match( v, "^([%+%-]?%d+)%,([%+%-]?%d+)$" )
if a then
return tonumber(a), tonumber(b)
elseif tonumber(v) then
return tonumber(v)
else
error( "Bad priority: "..v )
end
end
for k,v in pairs( argtable(...) ) do
if k==1 then N= tonumber(v) or HELP()
elseif k=="plain" then PLAIN= true
elseif k=="single" then SINGLE= v -- true/number
elseif k=="time" then TIME= true
elseif k=="prio" then PRIO_ODD, PRIO_EVEN= prio_param(v)
else HELP()
end
end
PRIO_ODD= PRIO_ODD or 0
PRIO_EVEN= PRIO_EVEN or 0
-- SAMPLE ADOPTED FROM Lua 5.1.1 test/sieve.lua --
-- the sieve of of Eratosthenes programmed with coroutines
-- typical usage: lua -e N=1000 sieve.lua | column
-- AK: Wrapped within a surrounding function, so we can pass it to Lanes
-- Note that coroutines can perfectly fine be used within each Lane. :)
--
-- AKa 20-Jul-2008: Now the wrapping to one function is no longer needed;
-- Lanes 2008 can take the used functions as upvalues.
--
local function sieve_lane(N,id)
if MSYS then
io.stderr:setvbuf "no"
end
-- generate all the numbers from 2 to n
local function gen (n)
return coroutine.wrap(function ()
for i=2,n do coroutine.yield(i) end
end)
end
-- filter the numbers generated by `g', removing multiples of `p'
local function filter (p, g)
return coroutine.wrap(function ()
while 1 do
local n = g()
if n == nil then return end
if math.mod(n, p) ~= 0 then coroutine.yield(n) end
end
end)
end
local ret= {} -- returned values: { 2, 3, 5, 7, 11, ... }
N=N or 1000 -- from caller
local x = gen(N) -- generate primes up to N
while 1 do
local n = x() -- pick a number until done
if n == nil then break end
--print(n) -- must be a prime number
table.insert( ret, n )
x = filter(n, x) -- now remove its multiples
end
io.stderr:write(id..(MSYS and "\n" or "\t")) -- mark we're ready
return ret
end
-- ** END OF LANE ** --
-- Keep preparation code outside of the performance test
--
local f_even= lanes.gen( "base,coroutine,math,table,io", -- "*" = all
{ priority= PRIO_EVEN }, sieve_lane )
local f_odd= lanes.gen( "base,coroutine,math,table,io", -- "*" = all
{ priority= PRIO_ODD }, sieve_lane )
io.stderr:write( "*** Counting primes 1.."..M.." "..N.." times ***\n\n" )
local t0= TIME and os.time()
if PLAIN then
io.stderr:write( "Plain (no multithreading):\n" )
for i=1,N do
local tmp= sieve_lane(M,i)
assert( type(tmp)=="table" and tmp[1]==2 and tmp[168]==997 )
end
else
if SINGLE then
io.stderr:write( (tonumber(SINGLE) and SINGLE or 1) .. " core(s):\n" )
lanes.single(SINGLE) -- limit to N cores (just OS X)
else
io.stderr:write( "Multi core:\n" )
end
if PRIO_ODD ~= PRIO_EVEN then
io.stderr:write( ( PRIO_ODD > PRIO_EVEN and "ODD" or "EVEN" )..
" LANES should come first (odd:"..PRIO_ODD..", even:"..PRIO_EVEN..")\n\n" )
else
io.stderr:write( "EVEN AND ODD lanes should be mingled (both: "..PRIO_ODD..")\n\n" )
end
local t= {}
for i=1,N do
t[i]= ((i%2==0) and f_even or f_odd) (M,i)
end
-- Make sure all lanes finished
--
for i=1,N do
local tmp= t[i]:join()
assert( type(tmp)=="table" and tmp[1]==2 and tmp[168]==997 )
end
end
io.stderr:write "\n"
if TIME then
local t= os.time() - t0
io.stderr:write( "*** TIMING: "..t.." seconds ***\n" )
end
--
-- end