aboutsummaryrefslogtreecommitdiff
path: root/test/functional/legacy/memory_usage_spec.lua
diff options
context:
space:
mode:
authorerw7 <erw7.github@gmail.com>2019-09-01 18:44:14 +0900
committererw7 <erw7.github@gmail.com>2020-05-07 16:47:41 +0900
commit1212390254c5de90513c0314efa94df7b9218590 (patch)
tree731d6f6e1198c6d4a50c0bb0e5507c597de9b088 /test/functional/legacy/memory_usage_spec.lua
parent17f067f4b4799dde06be78f9b7c9e7c7d60900a2 (diff)
downloadrneovim-1212390254c5de90513c0314efa94df7b9218590.tar.gz
rneovim-1212390254c5de90513c0314efa94df7b9218590.tar.bz2
rneovim-1212390254c5de90513c0314efa94df7b9218590.zip
vim-patch:8.1.1007: using closure may consume a lot of memory
Problem: Using closure may consume a lot of memory. Solution: unreference items that are no longer needed. Add a test. (Ozaki Kiichi, closes vim/vim#3961) https://github.com/vim/vim/commit/209b8e3e3bf7a4a3d102134124120f6c7f57d560
Diffstat (limited to 'test/functional/legacy/memory_usage_spec.lua')
-rw-r--r--test/functional/legacy/memory_usage_spec.lua151
1 files changed, 151 insertions, 0 deletions
diff --git a/test/functional/legacy/memory_usage_spec.lua b/test/functional/legacy/memory_usage_spec.lua
new file mode 100644
index 0000000000..14a7f2dbdd
--- /dev/null
+++ b/test/functional/legacy/memory_usage_spec.lua
@@ -0,0 +1,151 @@
+local helpers = require('test.functional.helpers')(after_each)
+local clear = helpers.clear
+local eval = helpers.eval
+local eq = helpers.eq
+local feed_command = helpers.feed_command
+local iswin = helpers.iswin
+local retry = helpers.retry
+local ok = helpers.ok
+local source = helpers.source
+
+local monitor_memory_usage = {
+ memory_usage = function(self)
+ local handle
+ if iswin() then
+ handle = io.popen('wmic process where processid=' ..self.pid..' get WorkingSetSize')
+ else
+ handle = io.popen('ps -o rss= -p '..self.pid)
+ end
+ return tonumber(handle:read('*a'):match('%d+'))
+ end,
+ op = function(self)
+ retry(nil, 10000, function()
+ local val = self.memory_usage(self)
+ if self.min > val then
+ self.min = val
+ elseif self.max < val then
+ self.max = val
+ end
+ table.insert(self.hist, val)
+ ok(#self.hist > 20)
+ local result = {}
+ for key,value in ipairs(self.hist) do
+ if value ~= self.hist[key + 1] then
+ table.insert(result, value)
+ end
+ end
+ table.remove(self.hist, 1)
+ self.last = self.hist[#self.hist]
+ eq(#result, 1)
+ end)
+ end,
+ dump = function(self)
+ return 'max: '..self.max ..', last: '..self.last
+ end,
+ monitor_memory_usage = function(self, pid)
+ local obj = {
+ pid = pid,
+ min = 0,
+ max = 0,
+ last = 0,
+ hist = {},
+ }
+ setmetatable(obj, { __index = self })
+ obj:op()
+ return obj
+ end
+}
+setmetatable(monitor_memory_usage,
+{__call = function(self, pid)
+ return monitor_memory_usage.monitor_memory_usage(self, pid)
+end})
+
+describe('memory usage', function()
+ local function check_result(tbl, status, result)
+ if not status then
+ print('')
+ for key, val in pairs(tbl) do
+ print(key, val:dump())
+ end
+ error(result)
+ end
+ end
+
+ local function isasan()
+ local version = eval('execute("version")')
+ return version:match('-fsanitize=[a-z,]*address')
+ end
+
+ before_each(clear)
+
+ --[[
+ Case: if a local variable captures a:000, funccall object will be free
+ just after it finishes.
+ ]]--
+ it('function capture vargs', function()
+ if isasan() then
+ pending('ASAN build is difficult to estimate memory usage')
+ end
+ if iswin() and eval("executable('wmic')") == 0 then
+ pending('missing "wmic" command')
+ elseif eval("executable('ps')") == 0 then
+ pending('missing "ps" command')
+ end
+
+ local pid = eval('getpid()')
+ local before = monitor_memory_usage(pid)
+ source([[
+ func s:f(...)
+ let x = a:000
+ endfunc
+ for _ in range(10000)
+ call s:f(0)
+ endfor
+ ]])
+ local after = monitor_memory_usage(pid)
+ -- Estimate the limit of max usage as 2x initial usage.
+ check_result({before=before, after=after}, pcall(ok, before.last < after.max))
+ check_result({before=before, after=after}, pcall(ok, before.last * 2 > after.max))
+ -- In this case, garbase collecting is not needed.
+ check_result({before=before, after=after}, pcall(eq, after.last, after.max))
+ end)
+
+ --[[
+ Case: if a local variable captures l: dict, funccall object will not be
+ free until garbage collector runs, but after that memory usage doesn't
+ increase so much even when rerun Xtest.vim since system memory caches.
+ ]]--
+ it('function capture lvars', function()
+ if isasan() then
+ pending('ASAN build is difficult to estimate memory usage')
+ end
+ if iswin() and eval("executable('wmic')") == 0 then
+ pending('missing "wmic" command')
+ elseif eval("executable('ps')") == 0 then
+ pending('missing "ps" command')
+ end
+
+ local pid = eval('getpid()')
+ local before = monitor_memory_usage(pid)
+ local fname = source([[
+ if !exists('s:defined_func')
+ func s:f()
+ let x = l:
+ endfunc
+ endif
+ let s:defined_func = 1
+ for _ in range(10000)
+ call s:f()
+ endfor
+ ]])
+ local after = monitor_memory_usage(pid)
+ for _ = 1, 3 do
+ feed_command('so '..fname)
+ end
+ local last = monitor_memory_usage(pid)
+ check_result({before=before, after=after, last=last},
+ pcall(ok, before.last < last.last))
+ check_result({before=before, after=after, last=last},
+ pcall(ok, last.last < after.max + (after.last - before.last)))
+ end)
+end)