Edge Rewrite
// HTMLRewriter · presentation

This page was redesigned at the edge.

Cloudflare fetched the original article and streamed it through HTMLRewriter to apply an entirely new visual system without rebuilding the source page.

// request.cf · coarse context

A page that knows where it met you.

Only coarse request metadata is shown. This demo does not display or persist visitor IP addresses.

Country
US
Cloudflare location
CMH
Connection
HTTP/2
Language
Not provided

Ray ID: a243ebebacc3fa14

Jump to content

Module:Sandbox/User:Waddie96/benchmark

From Wikipedia, the free encyclopedia

-- __NOINDEX__
--- Lua benchmarks: Compare performance of two functions over CPU time
--- [[Module:Benchmark]]

local ITERATIONS = 1e6 -- Default number of iterations
local p = {}
local _format = string.format
local html_create = mw.html.create
local _sub = string.sub
local _byte = string.byte
local _hash = mw.hash.hashValue
local _tostring = tostring


--[[ local input = '          "nowiki", ">", "D", "</", "nowiki", ">  |<", "pre", ">", "E|F", "</",      '
local whitespace = { [" "] = 1, ["\n"] = 1, ["\t"] = 1, ["\r"] = 1 }
local strlen = #input
local function func1()
	-- local out = string.gsub(input, "^%s*(.-)%s*$", "%1")
	local lowEnd
	for i = 1, strlen do
		if not whitespace[_sub(input, i, i)] then
			lowEnd = i
			break
		end
	end
	if not lowEnd then
		return ""
	end
	for i = strlen, 1, -1 do
		if not whitespace[_sub(input, i, i)] then
			local out = _sub(input, lowEnd, i)
			return out
		end
	end
	return out
end

local function func2()
	local low, high = 1, strlen

	-- find first non-whitespace
	while low <= strlen and whitespace[_byte(input, low)] do
		low = low + 1
	end

	-- if all whitespace
	if low > strlen then return "" end

	-- find last non-whitespace
	while high >= low and whitespace[_byte(input, high)] do
		high = high - 1
	end

	return _sub(input, low, high)
end ]]

local to_number = tonumber
local _type = type
local _lower = string.lower

local function func1(val)
    -- If your wiki uses non-ascii characters for any of "yes", "no", etc., you
	-- should replace "val:lower()" with "mw.ustring.lower(val)" in the
	-- following line.
	val = _type(val) == 'string' and _lower(val) or val
	if val == nil then
		return nil
	elseif val == true
		or val == 'yes'
		or val == 'y'
		or val == 'true'
		or val == 't'
		or val == 'on'
		or to_number(val) == 1
	then
		return true
	elseif val == false
		or val == 'no'
		or val == 'n'
		or val == 'false'
		or val == 'f'
		or val == 'off'
		or to_number(val) == 0
	then
		return false
	else
		return default
	end
end

local LOWER = string.lower
local TO_NUMBER = tonumber
local TYPE = type
local BOOLEAN_MAP = {
	yes = true,
	y = true,
	["true"] = true,
	t = true,
	on = true,
	["1"] = true,
	no = false,
	n = false,
	["false"] = false,
	f = false,
	off = false,
	["0"] = false,
}
    
local function func2(value)
    if value == nil then
        return nil
    end

    local valueType = TYPE(value)

    if valueType == 'boolean' then
        return value
    elseif valueType == 'string' then
        local lookupResult = BOOLEAN_MAP[LOWER(value)]
        if lookupResult ~= nil then
            return lookupResult
        end
    end

    -- Numeric check works for both numbers and numeric strings.
    local number = TO_NUMBER(value)
    if number == 1 then
        return true
    elseif number == 0 then
        return false
    end

    return defaultResponse
end

--- Format seconds into readable text
--- @param seconds number
--- @return string
local function format_time(seconds)
	if seconds < 0.001 then
		-- less than 1 millisecond → show in microseconds
		return _format("%.3f µs", seconds * 1e6)
	elseif seconds < 0.1 then
		-- between 1 ms and 100 ms → show in milliseconds
		return _format("%.3f ms", seconds * 1e3)
	else
		-- otherwise show seconds
		return _format("%.6f s", seconds)
	end
end

--- Run a function multiple times and measure total CPU time
--- @param iterations integer Number of iterations
--- @return number elapsed1 CPU time in seconds
--- @return number elapsed2 CPU time in seconds
--- @return integer correct
--- @return integer incorrect
local function measure(iterations)
    local out1, out2 = {}, {}
    local correct, incorrect = 0, 0
	local start1 = os.clock()
	for _ = 1, iterations do
		table.insert(out1, _hash('xxh64', _tostring(func1(1))))
	end
	local stop1 = os.clock()
	local start2 = os.clock()
	for _ = 1, iterations do
		table.insert(out2, _hash('xxh64', _tostring(func2(1))))
	end
	local stop2 = os.clock()
    for i = 1, iterations do
        if out1[i] == out2[i] and out1[i] ~= nil and out2[i] ~= nil then
            correct = correct + 1
        else
            incorrect = incorrect + 1
        end
    end
	return (stop1 - start1), (stop2 - start2), correct, incorrect, out1[1], out2[1]
end

local function make_table(t1, t2, n, faster, percent, correct, incorrect, out1, out2)
    local num = n
    n = mw.getCurrentFrame():callParserFunction( "formatnum", n )
	local tbl = html_create("table")
		:addClass("wikitable")
		:cssText("text-align: center;")
		:node(
			html_create("tr")
				:node(html_create("th"):wikitext("Function"))
				:done()
				:node(html_create("th"):wikitext("Total time"))
				:done()
				:node(html_create("th"):wikitext("Average time per iteration"))
				:done()
		)
		:done()
		:node(
			html_create("tr")
				:node(html_create("td"):wikitext("Function 1"))
				:done()
				:node(html_create("td"):wikitext(format_time(t1)))
				:done()
				:node(html_create("td"):wikitext(format_time(t1 / num)))
				:done()
		)
		:done()
		:node(
			html_create("tr")
				:node(html_create("td"):wikitext("Function 2"))
				:done()
				:node(html_create("td"):wikitext(format_time(t2)))
				:done()
				:node(html_create("td"):wikitext(format_time(t2 / num)))
				:done()
		)
		:done()
		:node(
			html_create("tr")
                :node(
                    html_create("td"):cssText("font-weight: bold;"):wikitext('Comparison')
                )
				:done()
				:node(
					html_create("td"):attr("colspan", "2"):wikitext(
                        _format("Function %s is <strong>%.2f%% faster</strong>", faster, percent)
                    )
                )
				:done()
		)
		:done()
		:node(
			html_create("tr")
                :node(
                    html_create("td"):cssText("font-weight: bold;"):wikitext('Iterations')
                )
				:done()
				:node(
					html_create("td"):attr("colspan", "2"):wikitext(
                        mw.getCurrentFrame()
                            :callParserFunction( "formatnum", n )
                    )
                )
				:done()
		)
		:done()
        :node(
			html_create("tr")
                :node(
                    html_create("td"):cssText("font-weight: bold;"):wikitext("Correct")
                )
				:done()
				:node(
					html_create("td"):attr("colspan", "2"):wikitext(
                        mw.getCurrentFrame()
                            :callParserFunction( "formatnum", correct )
                    )
                )
				:done()
		)
		:done()
        :node(
			html_create("tr")
                :node(
                    html_create("td"):cssText("font-weight: bold;"):wikitext('Incorrect')
                )
				:done()
				:node(
					html_create("td"):attr("colspan", "2"):wikitext(
                        mw.getCurrentFrame()
                            :callParserFunction( "formatnum", incorrect )
                    )
                )
				:done()
		)
		:done()
        :allDone()
    out1 = out1 or 'nil'
    out2 = out2 or 'nil'
	tbl_str = mw.allToString(tbl) .. '<strong>Output 1:</strong> ' .. func1() ..
                                     '<br/> <strong>Output 2:</strong> ' .. func2() ..
                                     '<br/> <strong>Output 1:</strong>'  .. _tostring(out1) ..
                                     '<br/> <strong>Output 2:</strong>'  .. _tostring(out2)
	return tbl_str
end

--- Main benchmark function
--- @param iterations? integer Number of iterations (default 10000)
--- @return string Wikitext table comparing results
function p.run(iterations)
    local totalstart = os.clock()
	local n = tonumber(iterations) or ITERATIONS
	local t1, t2, correct, incorrect, out1, out2 = measure(n)
	local faster = t1 < t2 and "1" or "2"
	local diff = math.abs(t1 - t2)
	local percent = (diff / math.max(t1, t2)) * 100

	local out = make_table(t1, t2, n, faster, percent, correct, incorrect, out1, out2)
    local totalend = os.clock()
    local totalsec = totalend - totalstart
    out = out .. '<br/> <strong>Total time taken:</strong> ' .. format_time(totalsec)
    return out
end

return p