diff options
author | erw7 <erw7.github@gmail.com> | 2019-09-01 18:44:14 +0900 |
---|---|---|
committer | erw7 <erw7.github@gmail.com> | 2020-05-07 16:47:41 +0900 |
commit | 1212390254c5de90513c0314efa94df7b9218590 (patch) | |
tree | 731d6f6e1198c6d4a50c0bb0e5507c597de9b088 /test/functional/legacy/memory_usage_spec.lua | |
parent | 17f067f4b4799dde06be78f9b7c9e7c7d60900a2 (diff) | |
download | rneovim-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.lua | 151 |
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) |