Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2009.02.15;
Скачать: [xml.tar.bz2];

Вниз

Оценил простоту и мощь Lua   Найти похожие ветки 

 
@!!ex ©   (2008-12-10 17:56) [0]

Вчера занялся внедрением скриптового движка в продукт.
Остановился на Lua, как мощном и гибком движке.

Сделал классовую обертку(может кому понадобится?). Покажите на ошибки, где накосячил?

unit uLua;

interface
uses lua, lualib, lauxlib,
    Dialogs;

type
 TLUAExternMethod = record
   Name:string;
   Address:lua_CFunction;
 end;

 TLUAParser = class
 protected
   FLUAState:Pointer;
   FExternMethods:array of TLUAExternMethod;
   Procedure   LuaInit;
   Procedure   LuaClose;
   Procedure   LuaUpdateMethods;
   Procedure   LuaAddMethod(const MethodName:string; Method:lua_CFunction);
 public
   Constructor Create();
   Destructor  Destroy(); override;
   Procedure   Clear();
   Procedure   LoadFromMemory(const Text:string);
   Function    Call(const ProcName:string; Params:array of const):boolean;
   Function    CallResultInt(const ProcName:string; Params:array of const):integer;
   Function    CallResultDoudble(const ProcName:string; Params:array of const):double;
   Function    CallResultString(const ProcName:string; Params:array of const):string;
   Procedure   AddMethod(const MethodName:string; Method:lua_CFunction);
 end;

implementation

{ TLUAParser }

procedure TLUAParser.AddMethod(const MethodName: string; Method: lua_CFunction);
begin
 SetLength(FExternMethods,Length(FExternMethods)+1);
 FExternMethods[Length(FExternMethods)-1].Name:=MethodName;
 FExternMethods[Length(FExternMethods)-1].Address:=Method;
 LuaAddMethod(MethodName,Method);
end;

Function TLUAParser.Call(const ProcName: string; Params: array of const):boolean;
var
 i:integer;
begin
 Result:=true;
 lua_getglobal(FLUAState, PChar(ProcName));
 for i := 0 to Length(Params) - 1 do begin
   case Params[i].VType of
     vtExtended:lua_pushnumber(FLUAState, Params[i].vExtended^);
     vtInteger:lua_pushnumber(FLUAState, Params[i].VInteger);
     vtString:lua_pushstring(FLUAState, PChar(Params[i].VString));
     vtPointer:lua_pushnumber(FLUAState, Params[i].VInteger);
   end;
 end;
 if (lua_pcall(FLUAState, Length(Params), 0, 0) <> 0) then begin
   ShowMessage("error running function "+ProcName+": "+lua_tostring(FLUAState, -1));
   lua_pop(FLUAState, 1);
   Result:=false;
 end;
end;

function TLUAParser.CallResultDoudble(const ProcName: string;
 Params: array of const): double;
begin
 if Call(ProcName,Params) then begin
   Result:=lua_tonumber(FLUAState,-1);
   lua_pop(FLUAState, 1);
 end;
end;

function TLUAParser.CallResultInt(const ProcName: string;
 Params: array of const): integer;
begin
 if Call(ProcName,Params) then begin
   Result:=Round(lua_tonumber(FLUAState,-1));
   lua_pop(FLUAState, 1);
 end;
end;

function TLUAParser.CallResultString(const ProcName: string;
 Params: array of const): string;
begin
 if Call(ProcName,Params) then begin
   Result:=lua_tostring(FLUAState,-1);
   lua_pop(FLUAState, 1);
 end;
end;

procedure TLUAParser.Clear;
begin
 LuaClose();
 LuaInit();
 LuaUpdateMethods;
end;

constructor TLUAParser.Create;
begin
 LuaInit();
end;

destructor TLUAParser.Destroy;
begin
 LuaClose();
 inherited;
end;

procedure TLUAParser.LoadFromMemory(const Text: string);
var
 error:integer;
begin
 error:=luaL_loadbuffer(FLUAState, PChar(Text), length(Text), "Chunk");
 if error=0 then
   error:=lua_pcall(FLUAState, 0, 0, 0);
 if (error<>0) then begin
   ShowMessage(lua_tostring(FLUAState, -1));
   lua_pop(FLUAState, 1);
 end
end;

procedure TLUAParser.LuaAddMethod(const MethodName: string;
 Method: lua_CFunction);
begin
 lua_pushcfunction(FLUAState, Method);
 lua_setglobal(FLUAState, PChar(MethodName));
end;

procedure TLUAParser.LuaClose;
begin
 lua_close(FLUAState);
end;

procedure TLUAParser.LuaInit;
begin
 FLUAState:=lua_open();
 luaopen_base(FLUAState);
 luaopen_table(FLUAState);
 luaopen_string(FLUAState);
 luaopen_math(FLUAState);
end;

procedure TLUAParser.LuaUpdateMethods;
var
 i:integer;
begin
 for I := 0 to Length(FExternMethods) - 1 do
   LuaAddMethod(FExternMethods[i].Name,FExternMethods[i].Address);
end;

end.


 
Palladin ©   (2008-12-11 09:01) [1]


> CallResultDoudble

ненравится мне этот тип :)


 
wicked ©   (2008-12-11 12:10) [2]

LoadFromStream/SaveToStream где?


 
@!!ex ©   (2008-12-11 13:50) [3]

> [1] Palladin ©   (11.12.08 09:01)

мда уж... опечатался. :))


> [2] wicked ©   (11.12.08 12:10)

а зачем?
Из текстовой строки грузит. а как эту строки доставали - ваше дело.
Меньше привязки к юнитам.


 
clickmaker ©   (2008-12-11 13:56) [4]

> Меньше привязки к юнитам.

тогда уж убери Dialogs и ShowMessage
а вместо
ShowMessage("error running function "+ProcName+": "+lua_tostring(FLUAState, -1));
вызывай событие
if Assigned(FOnError) then
 FOnError("error running function "+ProcName+": "+lua_tostring(FLUAState, -1));


 
Alkid   (2008-12-11 14:50) [5]


> @!!ex ©   (11.12.08 13:50) [3]

Ну, поток - это более высокий уровень абстракции, чем строка.
Реюзабельность класса от этого повысится.


 
tesseract ©   (2008-12-11 15:25) [6]

А как поначалу LUA оскоблял. Теперь заценил вот :-)


> Реюзабельность класса от этого повысится.


Тогда уже сразу абстракный  Stream вешать, можно и с нета и из архива напрямую качать.


 
Alkid   (2008-12-11 18:04) [7]


> tesseract ©   (11.12.08 15:25) [6]

Конечно абстрактный!


 
wicked ©   (2008-12-11 18:07) [8]

TStream и имелся в виду
на что-либо выше по иерархии завязываться нет смысла


 
@!!ex ©   (2008-12-11 21:32) [9]

> [4] clickmaker ©   (11.12.08 13:56)

Спасибо.


> [5] Alkid   (11.12.08 14:50)

В данном случае код из моего проекта, котоырй сейчас делаю. А в нем Стримы вообще не используются...

> [6] tesseract ©   (11.12.08 15:25)

Не ругал я Lua никогда! :)
В студию мои ругательства в сторону Lua!
говорил, что не имеет смысла использовать в своих проектах, потому что использование чужого кода не учит писать свой.


 
Городской Шаман   (2008-12-12 00:24) [10]


>
> @!!ex ©   (10.12.08 17:56)


Ты ж вроде на С++ перешёл?


 
@!!ex ©   (2008-12-12 09:41) [11]

> [10] Городской Шаман   (12.12.08 00:24)

Есть на С++ проекты. Но это не отменило Дельфи проектов. :)


 
VMcL ©   (2008-12-12 11:43) [12]

procedure TLUAParser.AddMethod(const MethodName: string; Method: lua_CFunction);
begin
SetLength(FExternMethods,Length(FExternMethods)+1);
FExternMethods[Length(FExternMethods)-1].Name:=MethodName;
FExternMethods[Length(FExternMethods)-1].Address:=Method;
LuaAddMethod(MethodName,Method);
end;


За такое перераспределение памяти уволить без выходного пособия.


 
@!!ex ©   (2008-12-12 12:30) [13]

> [12] VMcL ©   (12.12.08 11:43)

Ваши предложения?


 
@!!ex ©   (2008-12-12 12:33) [14]

Насколько я вижу, добавление элементов в C++ vector через push_back также дефрагментирует память. Разве есть другие варианты?
List чтоли делать? Или выделять сразу блоками? Эти способы тоже не панацея.


 
Palladin ©   (2008-12-12 12:34) [15]

TStringList


 
clickmaker ©   (2008-12-12 12:35) [16]

> SetLength(FExternMethods,Length(FExternMethods)+1);
> FExternMethods[Length(FExternMethods)-1].Name:=MethodName;
>
> FExternMethods[Length(FExternMethods)-1].Address:=Method;

Len := Length(FExternMethods) + 1;
SetLength(FExternMethods,Len);
Dec(Len, 2);
FExternMethods[Len].Name:=MethodName;
FExternMethods[Len].Address:=Method;

на самом деле, можно не каждый раз увеличивать массив на 1, а сразу задать некую длину, и если при добавлении она превышена, перераспределить заново на некую дельту. По аналогии с Capacity в TList


 
@!!ex ©   (2008-12-12 12:35) [17]

> [15] Palladin ©   (12.12.08 12:34)

Еще один "левый" класс. Ради чего?
И разве он также не делает realloc?


 
clickmaker ©   (2008-12-12 12:36) [18]

> Dec(Len, 2);

Dec(Len), конечно же )


 
@!!ex ©   (2008-12-12 12:37) [19]

> [16] clickmaker ©   (12.12.08 12:35)

Я так и делаю, в местах где данные меняются часто и критично по скорости.
Здесь скорость не критична, и данные меняются только один раз при инициализации.


 
clickmaker ©   (2008-12-12 12:37) [20]

> И разве он также не делает realloc?

не на каждое добавление
см. Capacity


 
Palladin ©   (2008-12-12 12:38) [21]


> @!!ex ©   (12.12.08 12:35) [17]

Ни TList ни TStringList не работают с дин. массивами, у них своя схема. Как раз блоками.


 
@!!ex ©   (2008-12-12 12:39) [22]

> [20] clickmaker ©   (12.12.08 12:37)

Я видел как сделан TList. :)
В [14] об этом написал.

> Или выделять сразу блоками?

Смысла не вижу в данном случае.


 
Palladin ©   (2008-12-12 12:41) [23]

Ну на самом деле, все зависит от ожидаемого количествоа этих Extern методов. Если не больше 100, можно сразу размерность выделить 100. И не нагружать менеджер Reallocaми


 
Городской Шаман   (2008-12-12 13:58) [24]


> VMcL ©   (12.12.08 11:43) [12]


Это те 90% кода которые влияют на 1% производительности. Так что конструкция вполне легитимная, сам так делаю. Данный код при регистрации всех функций не будет на современном процессоре и 0.1 мс забирать.

Так что я против premature optimization, которая приводит к шаманской работе программ - то она работает, то нет.


 
Городской Шаман   (2008-12-12 14:05) [25]


> Palladin ©   (12.12.08 12:41) [23]


Угу, только при подобных оптимизация получится модуль с производительность выше на 5-10% который будет влиять на 0.0001% производительности программы, с читабельностью кода стремящейся к 0%.

Сам говорю по опыту, тоже пробовал ловить проценты производительности там где не надо. Если так уж важна производительность то уд лучше перевести проект на C++ с нормальным компилятором, а не на тормознутом Ынтерпрайзе Delphi. Даже перекомпиляция на современном FPC увеличивает часто производительность в 300%.

Это конечно хорошо для того, кто хочет завязать проект на себя, но плохо для заказчика.


 
Palladin ©   (2008-12-12 14:12) [26]


>  с читабельностью кода стремящейся к 0%.

Хм, а как повредит единоразовое определение размера дин. массива на читабельность кода?


 
Servy ©   (2008-12-12 14:28) [27]

> лучше перевести проект на C++ с нормальным компилятором,
> а не на тормознутом Ынтерпрайзе Delphi


Зависит от критерия "нормальности". Для меня, компилятор, собирающий среднего размера проект минутами, нормальным не является :). Но, на вкус и цвет...

> Сделал классовую обертку(может кому понадобится?).


Мне понадобилось, давно собирался посмотреть, что за зверь такой этот lua, а тут повод появился. Thanks :). Взял на себя смелость внести некоторые изменения в приведенный модуль:

[*] Незначительные изменения для совместимости с D2009 (экспериментировал на ней). В частности, string -> AnsiString, там где нужно.
[+] Добавил "Adapter" делфевых объектов. В двух словах, теперь можно легко написать функцию, которая будет возвращать указатель на объект вовнутрь lua в виде user_data. Операции над ним перекрыты с помощью т.н. метатаблиц. Луа скрипт выглядит так:

local obj = trygetobject()
local name = obj.Name
obj.Name = "I was "..name.." some day, but i got NAME now!"
for i=1,10 do
 obj.Size = obj.Size + 1
end

         
 и он рулит объектом, описанным так:
 
 {$M+}
 TMyObj = class(TObject)
 private
   FName: string;
   FSize: Integer;
   procedure SetName(const Value: string);
   procedure SetSize(const Value: Integer);
 published
   property Size: Integer read FSize write SetSize;
   property Name: string read FName write SetName;

   constructor Create; virtual;
 end;
 {$M-}


Если у кого-нибудь есть идеи, как разрулить вызов методов из lua кода, был бы счастлив их услышать. Да, измененный модуль (с примером использования) лежит тут:
http://slil.ru/26429923


 
Servy ©   (2008-12-12 14:31) [28]

Забыл добавить, размер архива по ссылке 174 Кб.


 
VMcL ©   (2008-12-12 14:38) [29]

>>Городской Шаман   (12.12.08 13:58) [24]

У каждого свои тараканы.
Мои позволяют, единожды написав и протестировав код, возвращаться к нему только для добавления новой функциональности.


 
Городской Шаман   (2008-12-12 14:44) [30]


> Palladin ©   (12.12.08 14:12) [26]
>
>
> >  с читабельностью кода стремящейся к 0%.
>
> Хм, а как повредит единоразовое определение размера дин.
>  массива на читабельность кода?


Тем что в данном случае указатели на обработчики lua функций добавляются в динамике и для разных проблемных областей там может быть разное количество, а здесь нужно шаманить со статикой и пользователю компонента знать как он устроен внутри

Кроме того если Delphi уж берёт на "магию компилятора" динамические массивы то и компилятор должен заботится  и о выделении памяти блоками под него.

Во множестве реализаций std::vector выделяется память для динамического массива блоками определенной длинны и именно разработчик библиотеки берет на себя ответственность за распределение памяти внутри.

А если компилятор при вызове Setlength(,+1) каждый раз перераспределяет память, то это кривой компилятор.


 
Servy ©   (2008-12-12 14:56) [31]

> А если компилятор при вызове Setlength(,+1) каждый раз перераспределяет
> память, то это кривой компилятор.


Компилятор ничего не перераспределяет, он всего-лишь вставляет туда вызов соответствующей функции менеджера памяти. А менеджер памяти, если я правильно помню, можно заменить. Вроде как, именно это делал FastMM, недавно мелькало обсуждение.

В общем-то, спор ни о чем. Смысла оптимизировать при количестве добавляемых функций порядка 10-1000 по-видимому нет (добавление единократное, так как функции удаления из списка нет ^_^). С другой стороны, наличие такой оптимизации, тоже ничем не помешает (сложность кода повысится уж очень незначительно, говорить о нечитабельности или большой вероятности допустить там ошибку - преувеличение). Так что, пускай каждый и дальше делает, как ему нравится.


 
Городской Шаман   (2008-12-12 14:59) [32]


> VMcL ©   (12.12.08 14:38) [29]
>
> >>Городской Шаман   (12.12.08 13:58) [24]
>
> У каждого свои тараканы.
> Мои позволяют, единожды написав и протестировав код, возвращаться
> к нему только для добавления новой функциональности.


Тогда зачем нам Delphi давайте бухгалтерские программы писать сразу на assembler с использованием SSE и CUDA для повышения производительности.


 
Mystic ©   (2008-12-12 15:37) [33]

> Городской Шаман   (12.12.08 14:59) [32]

Во всем есть здравый смысл. Давай рассмотрим конкретно эту ситуацию.

1. Это библиотечный кот. Поэтому для данного кота вопрос оптимизации имеет большую роль в силу так называемого экспоненциального взрыва.

2. Скорость работы алгоритма зависит от длины массива. Предположим достаточно типичную ситуацию, что нам надо добавить 10 000 методов. В самом печальном случае на это потребуется примерно столько же релокаций памяти плюс копирование. Опять, отбрасывая копейки, средний размер массива 40 000 * 0.5 байт = 20 000 байт, умножая на 10 000 релокаций получаем копирование 200 Mb. Конечно, менеджер памяти у Delphi хороший, и не всякая релокация будет связана с копированием. В .NET это может оказаться более фатальным. Если там не предусмотрено специальных оптимизаций, то может понадобится 10 000 выделений памяти, несколько запусков сборщика мусора, а также копирование всего заявленного объема.

3. Если в данном случае использовать TStringList, код станет и короче, и понятнее. Но даже если и самому ввести дополнительную переменную и добавить условие, то это не никоим образом не сделает программу шаманской.


 
@!!ex ©   (2008-12-12 15:40) [34]

> [33] Mystic ©   (12.12.08 15:37)

Lua скрипт с 10 000 глобальных методов сдохнет сам по себе, независимо от реаллока.


 
Mystic ©   (2008-12-12 15:53) [35]

А чего ему сдыхать? Никто не говорит о том, что все эти методы будут вызываться.


 
@!!ex ©   (2008-12-12 16:04) [36]

> [35] Mystic ©   (12.12.08 15:53)

Обращение к функции идет через вызов метода lua_getglobal.
Это перебор всех глобальных значений с сравнением строк.
Для каждого вызова lua_getglobal в среднем нужно 5000 переборов.
конечно там не используется явный перебор и явное сравнение строк, что позволяет чуть ускорить это дело. Но суть не меняется. Такое количество глобальных значений будет зверско тормозить.
Тем более, что в глобальном пространстве хрянятся не только внешние функции, но и множество внутренних данных, которые работают через тот-же lua_getglobal. Так что это будет тормозить не только при вызове функций С из луа, но и при внутренних вызовах функций и переменных.

Опять же пример оторван от жизни. 10 000 внешних функций... Зачем? ВСе взаимодействие обычно происходит
через два десятка функций. Как правило при реализации стараются избежать частого вызова lua функций из кода и наоборот.

Оптимизацию такого плана считаю глупой. Оптимизировать надо там, где тормозит. Или где, хотя бы теоретически, может тормозить. ЗДесь не может тормозить даже теоретически. Потому что при достижении большого количества методов возникнут тормоза в других точках, и значительно более серьезные.


 
Городской Шаман   (2008-12-12 16:15) [37]


> Mystic ©   (12.12.08 15:37) [33]


По поводу (2) тогда стоит стоит добавить отдельный метод AddMethodList(array of ...) который и будет добавлять уже сформированный массив у "клиентского кода" из 10000 методов.

И вся проблема. Но второй метод если у будет использоваться, то только в 0.0001% случаев.


 
Городской Шаман   (2008-12-12 16:18) [38]


> Mystic ©   (12.12.08 15:37) [33]


С другой стороны - 10000 методов, да в WinApi поменьше функций будет.

Если 10000 методов то нужно не lua, а Java + Rhino JavaScript + мейнфрейм за 10000000$.


 
Mystic ©   (2008-12-12 16:42) [39]

> Городской Шаман   (12.12.08 16:15) [37]

Можно добавить и такой метод. Но если изначально размер списка методов установить равных 128, а при исчерпании лимита увеличивать размер ровно в два раза, то нам понадобится семь релокаций, при этом памяти будет скопировано ~ 100 кб, что всего в два с половиной раза больше первоначального размера списка. Разницы между твоим оптимизированным методом AddMethodList и добавлением методов в цикле никто и не заметит. Тем более, что этот массив, который передается в качестве аргумента, также надо будет сформировать. Очень может быть, что он читается из файла, и нам заранее неизвестно сколько в нем элементов. История повторяется :)

> Городской Шаман   (12.12.08 16:18) [38]

Конечно, если писать совсем не задумываясь об оптимизации, то никаких денег не хватит :) Вопрос еще и в том, что всякий раз, когда мне нужно добавить элемент к массиву, мне надо думать, к чему это приведет. Не проще ли забыть об этим потенциальных граблях и приучить себя писать дополнительные пару строк кода? А в случае TStringList и вовсе удалить пару строк. Ведь обходится большинство потенциальных проблем, а усилий никаких.


 
@!!ex ©   (2008-12-12 16:51) [40]

> [39] Mystic ©   (12.12.08 16:42)

Приведите код с TStringList, сравним читабельность.
Как уже говорилось выше - выделение блоками применятся(лично мной) там, где это актуально. Почему ЗДЕСЬ не актауально, уже объяснил.



Страницы: 1 2 вся ветка

Форум: "Прочее";
Текущий архив: 2009.02.15;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.58 MB
Время: 0.006 c
2-1230721519
demon
2008-12-31 14:05
2009.02.15
Как узнать создан ли класс?


15-1229694358
Xsh1
2008-12-19 16:45
2009.02.15
Сложная пятничная задачка с взвешивания


15-1229688631
Кое кто
2008-12-19 15:10
2009.02.15
Как переводиться have got?


15-1229697522
zorik
2008-12-19 17:38
2009.02.15
Проектирование бизнес-логики работы с БД


4-1205362998
Jolik
2008-03-13 02:03
2009.02.15
Как узнать выполняется моя функция в основной нитке...





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский