├── Dockerfile.test ├── LICENSE ├── Makefile ├── README.md ├── docker-compose.yml ├── example.jpg ├── lib └── resty │ └── perf.lua ├── lua-resty-perf-1.0.4-0.rockspec ├── spec └── lua-resty-perf_spec.lua └── test.lua /Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM openresty/openresty:xenial 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y \ 5 | git \ 6 | && mkdir /src \ 7 | && cd /src \ 8 | && git config --global url."https://".insteadOf git:// \ 9 | && luarocks install luacheck \ 10 | && luarocks install luacov \ 11 | && luarocks install luacov-coveralls \ 12 | && luarocks install busted \ 13 | && luarocks install lua-resty-perf 1.0.2-0 14 | 15 | CMD ["busted"] 16 | 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Leandro Moreira 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | bench: 2 | docker run -it --rm -v ${PWD}/test.lua:/test.lua -v ${PWD}/lib/resty/perf.lua:/lib/resty/perf.lua openresty/openresty:xenial resty /test.lua 3 | 4 | build-test: 5 | docker-compose build test 6 | 7 | test: 8 | docker-compose run --rm test 9 | 10 | lint: 11 | docker-compose run --rm lint 12 | 13 | .PHONY: build up down build-test test 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Resty Perf 2 | 3 | A simple resty lua library to benchmark memory and throughput of a function. 4 | 5 | ```lua 6 | local function mycode() 7 | local x = {} 8 | for i = 1, 1e3 do 9 | local now = ngx.now() 10 | now = now - 45 + i 11 | x[i] = now 12 | end 13 | return x 14 | end 15 | 16 | perf.perf_time("mycode cpu profiling", mycode) 17 | 18 | perf.perf_mem("mycode memory profiling", mycode) 19 | ``` 20 | To run it, you can use the openresty docker image: 21 | 22 | ```bash 23 | docker run -it --rm -v ${PWD}/test.lua:/test.lua -v ${PWD}/lib/resty/perf.lua:/lib/resty/perf.lua openresty/openresty:xenial resty /test.lua 24 | ``` 25 | 26 | ![perf command line result](example.jpg "A graph with experiments results") 27 | 28 | # Installing 29 | 30 | The module is available at [luarocks.](https://luarocks.org/modules/leandro/lua-resty-perf) 31 | 32 | ```bash 33 | luarocks install lua-resty-perf 34 | ``` 35 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | 3 | services: 4 | test: 5 | command: busted 6 | build: 7 | context: . 8 | dockerfile: Dockerfile.test 9 | volumes: 10 | - ".:/lua/" 11 | working_dir: "/lua" 12 | 13 | lint: 14 | command: bash -c "luacheck -q ." 15 | build: 16 | context: . 17 | dockerfile: Dockerfile.test 18 | volumes: 19 | - ".:/lua/" 20 | working_dir: "/lua" 21 | -------------------------------------------------------------------------------- /example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leandromoreira/lua-resty-perf/33adf57e6b94b8244f095a40dad008eb95a723e5/example.jpg -------------------------------------------------------------------------------- /lib/resty/perf.lua: -------------------------------------------------------------------------------- 1 | local _M = { 2 | _VERSION = "1.0.4-0", 3 | _AUTHOR = "Leandro Moreira", 4 | _LICENSE = "BSD-3-Clause License", 5 | _URL = "https://github.com/leandromoreira/lua-resty-perf", 6 | } 7 | 8 | local _update_time = function() 9 | end 10 | local _now = function() 11 | return os.clock() 12 | end 13 | 14 | -- when available, it uses openresty env 15 | if ngx then 16 | if ngx.update_time then 17 | _update_time = ngx.update_time 18 | end 19 | 20 | if ngx.now then 21 | _now = ngx.now 22 | end 23 | end 24 | 25 | _M.setup = function(opts) 26 | opts = opts or {} 27 | _M.result_cb = opts.result_cb or _M.result_cb 28 | _M.luajit_warmup_loop = opts.luajit_warmup_loop or _M.luajit_warmup_loop 29 | _M.disable_gc_during_tests = opts.disable_gc_during_tests or _M.disable_gc_during_tests 30 | _M.N = opts.N or _M.N 31 | _M.now = opts.now or _M.now 32 | _M.update_time = opts.update_time or _M.update_time 33 | end 34 | 35 | _M.default_opt = function() 36 | local opts = {} 37 | opts.result_cb = function(value, fmt_msg) -- function called after the test, its default behavior is to print to the sdtdout 38 | print(string.format(fmt_msg, value)) 39 | end 40 | opts.luajit_warmup_loop = 100 -- the number of execution to promote the code for the jit 41 | opts.disable_gc_during_tests = true -- disable gc during tests 42 | opts.N = 1e5 -- times to run the tests 43 | opts.now = _now 44 | opts.update_time = _update_time 45 | return opts 46 | end 47 | 48 | -- setting the default options 49 | _M.setup(_M.default_opt()) 50 | 51 | _M.perf_time = function(description, fn, result_cb, config) 52 | if description == nil then error("perf_time(description, fn): you must pass the description") end 53 | if fn == nil then error("perf_time(description, fn): you must pass the function") end 54 | if type(description) ~= "string" or type(fn) ~= "function" then error("perf_time(description, fn): description is a string and fn is a function") end 55 | 56 | config = config or {} 57 | result_cb = result_cb or config.result_cb or _M.result_cb 58 | local jit_loop = config.luajit_warmup_loop or _M.luajit_warmup_loop 59 | local disable_gc = config.disable_gc_during_tests or _M.disable_gc_during_tests 60 | local N = config.N or _M.N 61 | local update_time = config.update_time or _M.update_time 62 | local now = config.now or _M.now 63 | 64 | -- warming up lua jit 65 | for _ = 1, jit_loop do 66 | fn() 67 | end 68 | 69 | if disable_gc then collectgarbage("stop") end 70 | -- making sure time is up to date 71 | update_time() 72 | 73 | local start = now() -- ms precision 74 | for _ = 1, N do 75 | fn() 76 | end 77 | 78 | update_time() 79 | 80 | result_cb((now() - start)/N, ":: " .. description .. " :: took %.8f seconds per operation") 81 | 82 | if disable_gc then collectgarbage("restart") end 83 | end 84 | 85 | _M.perf_mem = function(description, fn, result_cb, config) -- result_cb, config are optional 86 | if description == nil then error("perf_mem(description, fn): you must pass the description") end 87 | if fn == nil then error("perf_mem(description, fn): you must pass the function") end 88 | if type(description) ~= "string" or type(fn) ~= "function" then error("perf_mem(description, fn): description is a string and fn is a function") end 89 | 90 | config = config or {} 91 | result_cb = result_cb or config.result_cb or _M.result_cb 92 | 93 | collectgarbage("stop") -- disabling gc 94 | local start = collectgarbage("count") 95 | 96 | fn() 97 | 98 | result_cb(collectgarbage("count") - start, ":: " .. description .. " :: used %d kb") 99 | collectgarbage("restart") -- re-enabling gc 100 | end 101 | 102 | return _M 103 | -------------------------------------------------------------------------------- /lua-resty-perf-1.0.4-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-resty-perf" 2 | version = "1.0.4-0" 3 | source = { 4 | url = "git://github.com/leandromoreira/lua-resty-perf", 5 | tag = "1.0.4" 6 | } 7 | description = { 8 | summary = "A simple resty lua library to benchmark memory and throughput of a function.", 9 | detailed = [[ 10 | Lua Resty Perf is a simple resty lua library to benchmark memory and throughput of a function. 11 | ]], 12 | homepage = "https://github.com/leandromoreira/lua-resty-perf", 13 | license = "BSD-3-Clause License " 14 | } 15 | build = { 16 | type = "builtin", 17 | modules = { 18 | ["resty.perf"] = "lib/resty/perf.lua" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spec/lua-resty-perf_spec.lua: -------------------------------------------------------------------------------- 1 | package.path = package.path .. ";lib/?.lua;spec/?.lua" 2 | 3 | -- in your code you should use: local perf = require "lua-resty-perf" 4 | local perf = require "resty.perf" 5 | 6 | local function mycode() 7 | local x = {} 8 | local now = os.time() 9 | for i = 1, 1e1 do 10 | now = now - 45 + i 11 | x[i] = now 12 | end 13 | os.execute("sleep " .. tonumber(0.001)) 14 | return x 15 | end 16 | 17 | 18 | describe("Lua Resty Perf", function() 19 | it("run memory and cpu profilling", function() 20 | perf.perf_time("mycode cpu profiling", function() 21 | mycode() 22 | end, nil, {N=1e3}) 23 | 24 | perf.perf_mem("mycode memory profiling", function() 25 | mycode() 26 | end, nil, {N=1e3}) 27 | end) 28 | end) 29 | -------------------------------------------------------------------------------- /test.lua: -------------------------------------------------------------------------------- 1 | package.path = package.path .. ";/lib/?.lua" 2 | 3 | local perf = require "resty.perf" 4 | 5 | local function mycode() 6 | local x = {} 7 | for i = 1, 1e3 do 8 | local now = ngx.now() 9 | now = now - 45 + i 10 | x[i] = now 11 | end 12 | return x 13 | end 14 | 15 | perf.perf_time("mycode cpu profiling", function() 16 | mycode() 17 | end) 18 | 19 | perf.perf_mem("mycode memory profiling", function() 20 | mycode() 21 | end) 22 | --------------------------------------------------------------------------------