Производительность Lua

Здесь обсуждается всё, что касается макросов.
User avatar
zg
zero-gravity
Posts: 5240
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 42 times
Contact:

trucer - замена одноимённого плагина

Post by zg » Mon 12 Sep, 2016 10:33

Latest post of the previous page:

John Doe wrote:Скорость "тримминга" отличается на порядок
у меня в 4,5 раза. а вот если пробелы на конце убрать, то match дольше в 6 раз. а ведь отсутствие пробелов в среднем будет гораздо чаще встречаться. я у себя в макро аналоге [esc] такое использую:

Code: Select all

KillSpaces1=(id,lineno,mms=false,spaces=true,eol)->
  line=editor.GetString id,lineno,0
  eol=(eol and line.StringEOL\len!>0) and eol or line.StringEOL
  replaces=0
  if spaces and (not mms or line.StringText!="-- ") then line.StringText,replaces=line.StringText\gsub "%s+$",""
  if replaces>0 or eol!=line.StringEOL then editor.SetString id,lineno,line.StringText,eol
оно примерно как mf.trim по скорости, если заменить на string.gsub — будет в два раза быстрее. ну и по идее на длинных строках за счёт отсутствия сравнения будет тоже небольшой выигрыш. судя по всему gsub больше тормозит с увеличением длины строки, чем сравнение.

User avatar
John Doe
Бюрократ
Posts: 11095
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 12 times
Been thanked: 66 times

trucer - замена одноимённого плагина

Post by John Doe » Mon 12 Sep, 2016 11:25

zg wrote: а вот если пробелы на конце убрать, то match дольше в 6 раз.
Действительно.
Но для наших целей можно использовать и find: string.find(str,"%s+$"), и тогда в обоих случаях будет быстрее чем trim.

Code: Select all

local function f1()
  local pos = string.find(str,"%s+$")
  return pos and string.sub(str,1,pos-1)
end

User avatar
raven428
Posts: 44
Joined: Sat 28 Mar, 2009 11:17
Has thanked: 9 times
Been thanked: 1 time

trucer - замена одноимённого плагина

Post by raven428 » Mon 12 Sep, 2016 11:38

John Doe wrote:
zg wrote: а вот если пробелы на конце убрать, то match дольше в 6 раз.
Действительно.
Но для наших целей можно использовать и find
а можно и обмануть match :) в таком виде скорость не падает:

Code: Select all

local function f1()
 return string.match(str..' ',"(.+)%s+$")
end
too much raVen over paradise...

User avatar
John Doe
Бюрократ
Posts: 11095
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 12 times
Been thanked: 66 times

trucer - замена одноимённого плагина

Post by John Doe » Mon 12 Sep, 2016 11:48

raven428 wrote: а можно и обмануть match
Это слишком хитро)
Вот так норм:(.*)%s*$

Перебирал разные квантификаторы (+-*) в обоих частях выражения, получал разные результаты.
Есть над чем подумать..

P.S.
А есть ещё и фаровские регэспы.
Хотя нет, они на уровне trim.

User avatar
raven428
Posts: 44
Joined: Sat 28 Mar, 2009 11:17
Has thanked: 9 times
Been thanked: 1 time

trucer - замена одноимённого плагина

Post by raven428 » Mon 12 Sep, 2016 12:20

по синтетическим тестам получается быстрей, а на реальном логе 95 мегабайт, 1.6 млн строк match работает даже на глаз раза в полтора, а то и два медленней, чем mf.trim.
too much raVen over paradise...

User avatar
John Doe
Бюрократ
Posts: 11095
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 12 times
Been thanked: 66 times

trucer - замена одноимённого плагина

Post by John Doe » Mon 12 Sep, 2016 12:36

raven428 wrote: а на реальном логе
Всё конечно может быть, но что-то мне не удалось пока подобрать строку, где бы match был медленнее.
Может выдерните из лога какой-нибудь пример?

У меня вот везде match в несколько раз быстрее, до 7

User avatar
raven428
Posts: 44
Joined: Sat 28 Mar, 2009 11:17
Has thanked: 9 times
Been thanked: 1 time

trucer - замена одноимённого плагина

Post by raven428 » Mon 12 Sep, 2016 12:54

в том-то и дело, что каждая отдельная строчка обрабатывается быстрей, а весь лог медленней. вероятно дело в нюансах с цпу и его буферами/кэшами. лог выложил тут.
too much raVen over paradise...

User avatar
John Doe
Бюрократ
Posts: 11095
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 12 times
Been thanked: 66 times

trucer - замена одноимённого плагина

Post by John Doe » Mon 12 Sep, 2016 13:07

Code: Select all

local filename = [[C:\APPs\Far3\changelog]]
local n = 1

local function bench(f,...)
  local start = Far.UpTime
  for i=1,n do
    f(...)
  end
  return (Far.UpTime-start)/1000
end

local function f1(str)
  return string.match(str,"(.*)%s*$")
end

local function f2(str)
  return mf.trim(str,2)
end

local function proc(fn)
  local file = assert(io.open(filename, "r"))
  for l in file:lines() do fn(l) end
  file:close()
end
far.Show(bench(proc,f1),bench(proc,f2))

Shmuel
Posts: 5096
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 11 times
Been thanked: 140 times

trucer - замена одноимённого плагина

Post by Shmuel » Mon 12 Sep, 2016 13:39

John Doe,
Этот бенчмарк искажается хэш-механизмом стрингов. Он в LuaJIT весьма своеобразный, о чём были репорты в LuaJIT mail-list, в том числе и мой репорт. Однако воз и ныне там.
Вот результаты вашего бенчмарка на первых 10 процентах лога raven428:

Code: Select all

╔════════════════╗   ╔════════════════╗
║  1│ 21.405     ║   ║  1│ 0.72       ║
║  2│ 2.781      ║   ║  2│ 0.828      ║
╚═ 2 arguments ══╝   ╚═ 2 arguments ══╝
Бенчмарк слева - для LuaJIT 2.0.4, бенчмарк справа - для Lua 5.1.5.

User avatar
raven428
Posts: 44
Joined: Sat 28 Mar, 2009 11:17
Has thanked: 9 times
Been thanked: 1 time

trucer - замена одноимённого плагина

Post by raven428 » Mon 12 Sep, 2016 13:49

а в макросах как-то можно переключаться между LuaJIT и Lua 5.1.5? результаты от всего файла у меня похожие:

Code: Select all

╔════════════════╗
║  1│ 89.302     ║
║  2│ 13.52      ║
╚═ 2 arguments ══╝l
too much raVen over paradise...

User avatar
John Doe
Бюрократ
Posts: 11095
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 12 times
Been thanked: 66 times

trucer - замена одноимённого плагина

Post by John Doe » Mon 12 Sep, 2016 14:21

И я погонял.
Странно всё конечно. До 10000 строк string.match лидирует, потом производительность падает.
collectgarbage не помогает.

Пробовал 100000 строк: сразу после старта фара производительность match чуть хуже чем trim.
Но повторный запуск теста ухудшает производительность match в 2 раза (на trim не влияет)
collectgarbage не помогает.

Но вот эта функция всё равно работает быстрее чем trim:

Code: Select all

local re = regex.new"(.*)\\s*$"
local function f1(str)
  return re:match(str)
end
(на малом числе строк string.match всё равно намного быстрее)

Shmuel
Posts: 5096
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 11 times
Been thanked: 140 times

trucer - замена одноимённого плагина

Post by Shmuel » Mon 12 Sep, 2016 14:23

raven428 wrote: а в макросах как-то можно переключаться между LuaJIT и Lua 5.1.5?
Увы, нельзя. Можно разве что держать отдельную копию Far с Lua 5.1.5.

User avatar
zg
zero-gravity
Posts: 5240
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 42 times
Contact:

trucer — замена одноимённого плагина

Post by zg » Mon 12 Sep, 2016 23:17

можно ещё такой вариант на скорость проверить:

Code: Select all

ffi=require"ffi"
C=ffi.C
ec=ffi.cast("struct PluginStartupInfo*",far.CPluginStartupInfo!).EditorControl
egs=ffi.new "struct EditorGetString"
egs.StructSize=ffi.sizeof egs
ess=ffi.new "struct EditorSetString"
ess.StructSize=ffi.sizeof ess

IsSpace=(char)->return char==32 or char==9
KillSpaces1=(id,lineno)->
  egs.StringNumber=lineno-1
  if (ec id,"ECTL_GETSTRING",0,egs)~=0
    ess.StringNumber=egs.StringNumber
    ess.StringLength=egs.StringLength
    ess.StringText=egs.StringText
    ess.StringEOL=egs.StringEOL
    while ess.StringLength>0
      if not IsSpace ess.StringText[ess.StringLength-1] then break
      ess.StringLength-=1
    if ess.StringLength~=egs.StringLength then ec id,"ECTL_SETSTRING",0,ess
update: использовать msvcrt.isspace видимо не самая хорошая идея, исправил на удаление только пробелов и табуляций.

User avatar
John Doe
Бюрократ
Posts: 11095
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 12 times
Been thanked: 66 times

Производительность Lua

Post by John Doe » Tue 13 Sep, 2016 12:42

Code: Select all

local ffi = require("ffi")
local C = ffi.C
local ec = ffi.cast("struct PluginStartupInfo*", far.CPluginStartupInfo()).EditorControl
local egs = ffi.new("struct EditorGetString")
egs.StructSize = ffi.sizeof(egs)
local ess = ffi.new("struct EditorSetString")
ess.StructSize = ffi.sizeof(ess)
local IsSpace
IsSpace = function(char)
  return char == 32 or char == 9
end
local KillSpaces1
KillSpaces1 = function(id, lineno)
  egs.StringNumber = lineno - 1
  if (ec(id, "ECTL_GETSTRING", 0, egs)) ~= 0 then
    ess.StringNumber = egs.StringNumber
    ess.StringLength = egs.StringLength
    ess.StringText = egs.StringText
    ess.StringEOL = egs.StringEOL
    while ess.StringLength > 0 do
      if not IsSpace(ess.StringText[ess.StringLength - 1]) then
        break
      end
      ess.StringLength = ess.StringLength - 1
    end
    if ess.StringLength ~= egs.StringLength then
      return ec(id, "ECTL_SETSTRING", 0, ess)
    end
  end
end

local function KillSpaces(eid,lineno)
  local str, eol = editor.GetString(eid,lineno,3);
  local trm = mf.trim(str,2);
  if trm and str~=trm then
    editor.SetString(eid,lineno,trm,eol);
  end;
end


local limit = 10 --seconds
local function bench(f)
  local ei = editor.GetInfo()
  local eid = ei.EditorID
  local start = far.FarClock()
  local goal = start+limit*10^6
  local c = 0
  for i=1,ei.TotalLines do
    c=c+1
    f(eid,i)
    if c==1000 or i==ei.TotalLines then
      local time = far.FarClock()
      if time>goal or i==ei.TotalLines then
        far.Show(math.ceil(i/1000),math.ceil((time-start)/10^6))
        return
      end
      c=0
    end
  end;
end
MenuItem{--  description="";
  guid="AC94873C-C652-4D04-AB1E-DE4DF4EFB455";
  menu="Plugins";
  area="Editor";
  text="~bench~KillSpaces";
  action=function()
    bench(KillSpaces)
  end;
}
MenuItem{--  description="";
  guid="25E1ED12-6E51-430B-9E98-4643E5EDB1BF";
  menu="Plugins";
  area="Editor";
  text="~bench~KillSpaces1";
  action=function(OpenFrom,Item)
    bench(KillSpaces1)
  end;
}
Выводит сколько тысяч строк обработано за заданный промежуток времени, ограничение выставлено в 10 секунд.
У меня получилось что разница в производительности - 30 раз :shock:

И тормозит вовсе даже не trim..

User avatar
zg
zero-gravity
Posts: 5240
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 42 times
Contact:

Производительность Lua

Post by zg » Tue 13 Sep, 2016 13:41

John Doe wrote:У меня получилось что разница в производительности - 30 раз :shock:

И тормозит вовсе даже не trim..
конвертация utf8<->utf16. получить строку, отправить в трим, получить из трим, если не совпало - установить. поэтому встроенные и работают быстрее, пока не уткнутся в луаджитовское хеширование.
но на самом деле это не сильно актуально, на практике мало кому надо обрабатывать по 100 мб за раз.

User avatar
John Doe
Бюрократ
Posts: 11095
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 12 times
Been thanked: 66 times

Производительность Lua

Post by John Doe » Tue 13 Sep, 2016 15:38

John Doe wrote: И тормозит вовсе даже не trim..
Я вот другое пытаюсь сказать.
Мы тут trim оптимизируем, в то время как GetString похоже на два порядка тяжелее.
В примере выше даже если убрать trim напрочь local trm = str никак ситуация не меняется.

Тупо вызов editor.GetString в 30 раз медленнее чем прямое использование ECTL_SETSTRING.
zg wrote: но на самом деле это не сильно актуально, на практике мало кому надо обрабатывать по 100 мб за раз.
Это да.

User avatar
zg
zero-gravity
Posts: 5240
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 42 times
Contact:

Производительность Lua

Post by zg » Tue 13 Sep, 2016 16:37

у меня, если в KillSpaces закоментить всё, кроме первой строки, время в два раза увеличивается на тестовом логе. не знаю, что и думать.

Post Reply

Return to “Обсуждение макросов”