local d={}d.__index=d wait(3)local h=(require(script:WaitForChild("\082\101\097\100\097\098\108\101\078\117\109\098\101\114\115"))).new()local e=game:GetService("\082\117\110\083\101\114\118\105\099\101")local function O(d)return type(d)=="\102\117\110\099\116\105\111\110"end local function t(...)local d={...}local h=select("\035",...)for h=1,h,1 do if not O(d[h])then return false end end return true end local function x(d)return tonumber(d)end local function Q(d)return x(d)and tonumber(d)==math.floor(d)end local function M(d,h)return((h-d)/d)*100 end function d.new(e,O,t,M,a,y)O=O or 1 e=e or 1000.0 M=M==nil and true or M a=a==nil and h or a y=y or.1 assert(Q(e),"\087\114\111\110\103 \097\114\103\117\109\101\110\116 \103\105\118\101\110 \102\111\114 \099\121\099\108\101\115\044 \101\120\112\101\099\116\101\100 \105\110\116\101\103\101\114 \098\117\116 \114\101\099\101\105\118\101\100 "..typeof(e))assert(x(O),"\119\114\111\110\103 \097\114\103\117\109\101\110\116 \103\105\118\101\110 \102\111\114 \100\117\114\097\116\105\111\110\044 \101\120\112\101\099\116\101\100 \097 \110\117\109\098\101\114 \098\117\116 \114\101\099\101\105\118\101\100 "..typeof(O))assert(a==false or a.format,"\087\114\111\110\103 \097\114\103\117\109\101\110\116 \103\105\118\101\110 \102\111\114 \114\101\097\100\097\098\108\101\078\117\109\098\101\114\115\044 \101\120\112\101\099\116\101\100 \102\097\108\115\101\047\082\101\097\100\097\098\108\101\078\117\109\098\101\114\115 \111\098\106\101\099\116 \098\117\116 \114\101\099\101\105\118\101\100 "..typeof(a))assert(x(y),"\087\114\111\110\103 \097\114\103\117\109\101\110\116 \103\105\118\101\110 \102\111\114 \110\111\089\105\101\108\100\084\105\109\101\044 \101\120\112\101\099\116\101\100 \110\117\109\098\101\114 \098\117\116 \114\101\099\101\105\118\101\100 "..typeof(y))return setmetatable({duration=O;cycles=e,showProgress=t;showFullInfo=M,readableNumbers=a;noYieldTime=y},d)end function d.compare(O,d,h,e,...)e=e or 0 assert(t(d,h),"\087\114\111\110\103 \097\114\103\117\109\101\110\116 \103\105\118\101\110 \102\111\114 \102\117\110\099\049\047\102\117\110\099\050\044 \101\120\112\101\099\116\101\100 \102\117\110\099\116\105\111\110\115 \098\117\116 \114\101\099\101\105\118\101\100 "..(typeof(d)..(" \097\110\100 "..typeof(h))))assert(Q(e),"\087\114\111\110\103 \097\114\103\117\109\101\110\116 \103\105\118\101\110 \102\111\114 \102\117\110\099\105\111\110\049\065\109\111\117\110\116\065\114\103\115\044 \101\120\112\101\099\116\101\100 \105\110\116\101\103\101\114 \098\117\116 \114\101\099\101\105\118\101\100 "..typeof(d))O:print(("\080\101\114\102\111\114\109\105\110\103 \097 \099\111\109\112\097\114\105\115\111\110 \098\101\116\119\101\101\110 \102\117\110\099\116\105\111\110\049 \097\110\100 \102\117\110\099\116\105\111\110\050 \119\105\116\104 \037\115 \099\121\099\108\101\115 \097\110\100 \097 \100\117\114\097\116\105\111\110 \111\102 \037\115\115"):format(O:toReadable(O.cycles,O.duration)))local x,a=O:getAvg(d,unpack({...},1,e))O:print(("\070\117\110\099\116\105\111\110\049 \104\097\115 \097\110 \097\118\101\114\097\103\101 \111\102 \037\115\115 \112\101\114 \099\121\099\108\101 \097\110\100 \116\111\111\107 \105\110 \116\111\116\097\108 \037\115\115"):format(O:toReadable(x,a)))local y,u=O:getAvg(h,select(e+1,...))O:print(("\070\117\110\099\116\105\111\110\050 \104\097\115 \097\110 \097\118\101\114\097\103\101 \111\102 \037\115\115 \112\101\114 \099\121\099\108\101 \097\110\100 \116\111\111\107 \105\110 \116\111\116\097\108 \037\115\115"):format(O:toReadable(y,u)))local v=M(x,y)print(("\070\117\110\099\116\105\111\110\049 \097\118\101\114\097\103\101 \099\121\099\108\101 \105\115 \037\046\050\102\037\037 \037\115 \116\104\097\110 \102\117\110\099\116\105\111\110\050\033"):format(v,v<0 and"\115\108\111\119\101\114"or"\102\097\115\116\101\114"))local n,k=O:getCycles(d,unpack({...},1,e))O:print(("\102\117\110\099\116\105\111\110\049 \119\097\115 \099\097\108\108\101\100 \037\115 \116\105\109\101\115 \040\037\115\047\115\041"):format(O:toReadable(n,k)))local g,m=O:getCycles(h,select(e+1,...))O:print(("\102\117\110\099\116\105\111\110\050 \119\097\115 \099\097\108\108\101\100 \037\115 \116\105\109\101\115 \040\037\115\047\115\041"):format(O:toReadable(g,m)))local U=M(m,k)print(("\070\117\110\099\116\105\111\110\049 \104\097\115 \037\046\050\102\037\037 \037\115 \099\121\099\108\101\115\047\115 \116\104\097\110 \102\117\110\099\116\105\111\110\050\033"):format(U,U<0 and"\108\101\115\115"or"\109\111\114\101"))end function d.getAvg(h,d,...)assert(O(d),"\087\114\111\110\103 \097\114\103\117\109\101\110\116 \103\105\118\101\110 \102\111\114 \102\117\110\099\044 \101\120\112\101\099\116\101\100 \102\117\110\099\116\105\111\110 \098\117\116 \114\101\099\101\105\118\101\100 "..typeof(d))local t=0 local x=0 if h.showProgress then print("\067\097\108\099\117\108\097\116\105\110\103 \097\118\101\114\097\103\101 \099\121\099\108\101")end while t<h.cycles do local O=0 while O<h.noYieldTime and t<h.cycles do local h=os.clock()d(...)O+=os.clock()-h t+=1 end e.Heartbeat:Wait()x+=O if h.showProgress then print(("\037\100\037\037 \100\111\110\101\033"):format((t/h.cycles)*100))end end return x/h.cycles,x end function d.getCycles(h,d,...)assert(O(d),"\087\114\111\110\103 \097\114\103\117\109\101\110\116 \103\105\118\101\110 \102\111\114 \102\117\110\099\044 \101\120\112\101\099\116\101\100 \102\117\110\099\116\105\111\110 \098\117\116 \114\101\099\101\105\118\101\100 "..typeof(d))local t=0 local x=0 if h.showProgress then print("\067\097\108\099\117\108\097\116\105\110\103 \097\109\111\117\110\116 \111\102 \099\121\099\108\101\115 \102\111\114 \116\104\101 \103\105\118\101\110 \100\117\114\097\116\105\111\110")end while x<h.duration do local O=0 while O<h.noYieldTime and x+O<h.duration do local h=os.clock()d(...)O+=os.clock()-h t+=1 end e.Heartbeat:Wait()x+=O if h.showProgress then print(("\037\100\037\037 \100\111\110\101\033"):format((x/h.duration)*100))end end return t,t/x,x end function d.benchmark(h,d,...)assert(O(d),"\087\114\111\110\103 \097\114\103\117\109\101\110\116 \103\105\118\101\110 \102\111\114 \102\117\110\099\044 \101\120\112\101\099\116\101\100 \102\117\110\099\116\105\111\110 \098\117\116 \114\101\099\101\105\118\101\100 "..typeof(d))h:print(("\080\101\114\102\111\114\109\105\110\103 \097 \098\101\110\099\104\109\097\114\107 \119\105\116\104 \037\115 \099\121\099\108\101\115 \097\110\100 \097 \100\117\114\097\116\105\111\110 \111\102 \037\115\115"):format(h:toReadable(h.cycles,h.duration)))print(("\084\104\101 \102\117\110\099\116\105\111\110 \116\111\111\107 \111\110 \097\118\101\114\097\103\101 \037\115\115 \112\101\114 \099\121\099\108\101 \097\110\100 \116\111\111\107 \105\110 \116\111\116\097\108 \037\115\115"):format(h:toReadable(h:getAvg(d,...))))print(("\084\104\101 \102\117\110\099\116\105\111\110 \119\097\115 \099\097\108\108\101\100 \037\115 \116\105\109\101\115 \040\037\115\047\115\041 \102\111\114 \116\104\101 \103\105\118\101\110 \100\117\114\097\116\105\111\110 \037\115\115 "):format(h:toReadable(h:getCycles(d,...))))end function d.print(d,...)if d.showFullInfo then print(...)end end function d.toReadable(d,...)if d.readableNumbers then return d.readableNumbers:format(...)end return...end return d.new()
-- @Author: VerdommeMan, see https://github.com/VerdommeMan/Benchmarker for more information 
-- @Version: 1.0.0

local Benchmarker = {}
Benchmarker.__index = Benchmarker

wait(3) -- slows down the initial require so that the game gets time to load fully first, this allows to the benchmarks to be more accurate 

local readableNumbers = require(script:WaitForChild("ReadableNumbers")).new()

local RunService = game:GetService('RunService')

local function isFunction(arg)
    return type(arg) == "function"
end

local function areFunctions(...)
    local args = {...}
    local n = select("#", ...)

    for i = 1, n do
        if not isFunction(args[i]) then
            return false
        end
    end
    return true
end

-- these are modified to allow string-number coersion
local function isNumber(arg)
    return tonumber(arg)
end

local function isInt(arg)
    return isNumber(arg) and tonumber(arg) == math.floor(arg)
end

local function getPercentage(old, new)
    return (new - old) / old * 100   
end

function Benchmarker.new(cycles, duration, showProgress, showFullInfo, ReadableNumbers, noYieldTime) -- set the configuration
     -- set defaults
    duration = duration or 1
    cycles = cycles or 1e3
    showFullInfo = showFullInfo == nil and true or showFullInfo -- defaults to true
    ReadableNumbers = ReadableNumbers == nil and readableNumbers or ReadableNumbers 
    noYieldTime = noYieldTime or 0.1
    -- type checking
    assert(isInt(cycles), "Wrong argument given for cycles, expected integer but received " .. typeof(cycles))
    assert(isNumber(duration), "wrong argument given for duration, expected a number but received " .. typeof(duration))
    assert(ReadableNumbers == false or ReadableNumbers.format, "Wrong argument given for readableNumbers, expected false/ReadableNumbers object but received " .. typeof(ReadableNumbers))
    assert(isNumber(noYieldTime), "Wrong argument given for noYieldTime, expected number but received " .. typeof(noYieldTime))

    return setmetatable({
        duration = duration,
        cycles = cycles,
        showProgress = showProgress,
        showFullInfo = showFullInfo,
        readableNumbers = ReadableNumbers,
        noYieldTime = noYieldTime
    }, Benchmarker)
end

function Benchmarker:compare(func1, func2, function1AmountArgs, ...)
    function1AmountArgs = function1AmountArgs or 0
    assert(areFunctions(func1, func2), "Wrong argument given for func1/func2, expected functions but received " .. typeof(func1) .. " and " .. typeof(func2))
    assert(isInt(function1AmountArgs), "Wrong argument given for funcion1AmountArgs, expected integer but received " .. typeof(func1)  )

    self:print(("Performing a comparison between function1 and function2 with %s cycles and a duration of %ss"):format(self:toReadable(self.cycles, self.duration)))
    local avg1, totalTime1 = self:getAvg(func1, unpack({...}, 1, function1AmountArgs) )
    self:print(("Function1 has an average of %ss per cycle and took in total %ss"):format(self:toReadable(avg1, totalTime1)))
    local avg2, totalTime2 = self:getAvg(func2, select(function1AmountArgs + 1, ...))
    self:print(("Function2 has an average of %ss per cycle and took in total %ss"):format(self:toReadable(avg2, totalTime2)))
    local p = getPercentage(avg1, avg2)
    print(("Function1 average cycle is %.2f%% %s than function2!"):format(p, p < 0 and "slower" or "faster"))
    
    local totalAmount1, amountPerS1 = self:getCycles(func1, unpack({...}, 1, function1AmountArgs))
    self:print(("function1 was called %s times (%s/s)"):format(self:toReadable(totalAmount1, amountPerS1)))
    local totalAmount2, amountPerS2 = self:getCycles(func2, select(function1AmountArgs + 1, ...))
    self:print(("function2 was called %s times (%s/s)"):format(self:toReadable(totalAmount2, amountPerS2)))
    local p2 = getPercentage( amountPerS2, amountPerS1)
    print(("Function1 has %.2f%% %s cycles/s than function2!"):format(p2, p2 < 0 and "less" or "more"))
end

function Benchmarker:getAvg(func, ...)
    assert(isFunction(func), "Wrong argument given for func, expected function but received " .. typeof(func))

    local amount = 0
    local totalTime = 0

    if self.showProgress then
        print("Calculating average cycle")
    end

    while amount < self.cycles do
        local subTime = 0
        while subTime < self.noYieldTime and amount < self.cycles do
            local startTime = os.clock()
            func(...)
            subTime +=  os.clock() - startTime
            amount += 1
        end
        RunService.Heartbeat:Wait()
        totalTime += subTime

        if self.showProgress then
            print(("%d%% done!"):format(amount / self.cycles * 100))
        end
    end 

    return totalTime / self.cycles, totalTime
end

function Benchmarker:getCycles(func, ...) --need better name, gets the the cycles for the set duration
    assert(isFunction(func), "Wrong argument given for func, expected function but received " .. typeof(func))

    local amount = 0
    local totalTime = 0

    if self.showProgress then
        print("Calculating amount of cycles for the given duration")
    end

    while totalTime < self.duration do
        local subTime = 0
        while subTime < self.noYieldTime and totalTime + subTime < self.duration do
            local startTime = os.clock()
            func(...)
            subTime +=  os.clock() - startTime
            amount += 1
        end
        RunService.Heartbeat:Wait()
        totalTime += subTime

        if self.showProgress then
            print(("%d%% done!"):format(totalTime/self.duration *100))
        end
    end
    
    return amount, amount / totalTime, totalTime 
end

function Benchmarker:benchmark(func, ...)
    assert(isFunction(func), "Wrong argument given for func, expected function but received " .. typeof(func))

    self:print(("Performing a benchmark with %s cycles and a duration of %ss"):format(self:toReadable(self.cycles, self.duration)))
    print(("The function took on average %ss per cycle and took in total %ss"):format(self:toReadable(self:getAvg(func, ...))))
    print(("The function was called %s times (%s/s) for the given duration %ss "):format(self:toReadable(self:getCycles(func, ...))))
end

function Benchmarker:print(...)
    if self.showFullInfo then
        print(...)
    end
end

function Benchmarker:toReadable(...)
    if self.readableNumbers then
        return self.readableNumbers:format(...)
    end
    return ...
end

return Benchmarker.new() -- set default config