삽질하는플머

'탐구생활/Lua'에 해당되는 글 3건

  1. LuaJit_Research
  2. 루아 디버깅 테스트
  3. LNUM 패치가 적용된 arm-wince 용 루아 5.1.4

LuaJit_Research

탐구생활/Lua
2010. 2. 2
-------------

맵서버 전체적으로 루아가 사용되면서 일본 서버에서는 한 번도 나타난 적 없는 길드창고 복사버그 등이 발생할 수 있다는 것이 확인되었다.
캐릭터가 틱을 갖게 되는 시간이 벌어지는 것이 원인으로 보이는데... 안전장치는 논외로 하더라도 근본적으로 루아의 성능문제를 고민할 시점이라는 이야기...

LuaJit 을 희망삼아 삽질을 시작 해 보자. 먼저 http://luajit.org/ 에서 안정버전인 LuaJIT-1.1.5 를 내려받는다.
이 버전은 현재 사용중인 Lua 5.1.4 를 기반으로 한 것이다.

간만에 빌드하는 것이라 기억이 가물가물하네...

루트의 Makefile 은 인스톨에 대한 부분을, src의 Makefile 은 타겟에 대한 정보를 담고 있다.
mingw 도 들어있으니 한 번 시도.



MSYS 에서 make mingw 타이핑. 별로 고민할 것도 없이 lua51.dll 과 luajit.exe 가 생성된다.
생성된 DLL은 258kb 로, 루아 5.1.4 의 152kb 에 비해 100kb 정도 늘어나있다. -Os 옵션을 주고 컴파일하면 루아 코어에 32kb 만 증가된다고 하던데...

src/Makefile 내의 CFLAG 를 -O3 를 -Os 로 변경하니 용량이 170kb 로 줄어든다. 캬~~
지금은 성능이 문제니, 원래대로 -O3 로 해 둔다.

컴파일된 DLL에는 예전에 못보던 함수 몇 개가 더 보인다.


Exports from lua51.dll
  127 exported name(s), 127 export addresse(s).  Ordinal base is 1.
  Sorted by Name:
    RVA      Ord. Hint Name
    -------- ---- ---- ----
    0001E480    1 0000 luaJIT_compile
    0001E690    2 0001 luaJIT_setmode
    0001E870    3 0002 luaJIT_version_1_1_5
........
    00033B40  122 0079 luaopen_jit
........


아마도 이 물건들이 JIT 을 구동하기 위한 함수들인 것으로 보인다.




DLL에 버전 정보가 없으니 매우 썰렁한 느낌이 드는군. 적당한 버전 정보를 추가 해 보세~~
조금 어거지로 끼워맞춘 감이 있지만... 뭐 되기만 하면 되지.

일단 원본 Makefile 은 백업한다.

lua_dll_win32.rc 파일을 만들고 내용은 다음과 같이 채운다.

1 VERSIONINFO
 FILEVERSION 2010,2,2,1
 PRODUCTVERSION 2010,2,2,1
 {
  BLOCK "StringFileInfo"
  {
   BLOCK "040904E4"
   {
    VALUE "Comments", "LuaJIT 1.1.5 based on Lua 5.1.4\000"
    VALUE "CompanyName", "\000"
    VALUE "FileDescription", "LuaJIT 1.1.5\000"
    VALUE "FileVersion", "2010.2.2.1\000"
    VALUE "InternalName", "\000"
    VALUE "LegalCopyright", "Compiled by ORANKE\000"
    VALUE "LegalTrademarks", "\000"
    VALUE "OriginalFilename", "lua51.dll\000"
    VALUE "ProductName", "\000"
    VALUE "ProductVersion", "2010.2.2.1\000"
   }
  }
  BLOCK "VarFileInfo"
  {
   VALUE "Translation", 0x0409, 0x04E4
  }
 }


 Makefile 의 mingw: 부 상단에 다음과 같이 리소스 컴파일 코드를 기록한다. (154 라인 근처)

lua_dll_win32_res:
 $(RC) -i lua_dll_win32.rc -J rc -o lua_dll_win32_res.o -O coff


mingw: 부에 다음과 같이 리소스 컴파일 코드를 넣는다.

mingw:
 $(MAKE) lua_dll_win32_res a "MYCFLAGS=-DLUA_BUILD_AS_DLL"
 $(MAKE) "LUA_A=lua51.dll" "LUA_T=luajit.exe" \
 "AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \
 "MYCFLAGS=-DLUA_BUILD_AS_DLL -maccumulate-outgoing-args" \
 "MYLIBS=" "MYLDFLAGS=-s" luajit.exe



$(LUA_A): 부분에서 위에서 만들어진 lua_dll_win32_res.o 가 추가되도록 해준다.

$(LUA_A): $(CORE_O) $(LIB_O) lua_dll_win32_res.o
 $(AR) $@ $?
 $(RANLIB) $@


이제 컴파일한 결과는 다음과 같다. 






추가로, mingw: 부의 마지막줄에 다음을 추가하면, DLL 의존성 없는 luac.exe 파일을 얻을 수 있다.  

$(MAKE) "LUAC_T=luac.exe" luac.exe





이제 실제로 속도를 테스트 해 보자. 기존 루아 5.1.4 와 분리하기 위해 dll 명칭은 lua51.dll 을 그대로 사용하자. 

루아 환경 생성시 luaopen_jit() 로 JIT 라이브러리를 로드하고 luaJIT_setmode(L, 0, LUAJIT_MODE_ENGINE or LUAJIT_MODE_ON) 로 JIT 을 켜준다. 

컴파일되지 않은 루아파일 중 덩치가 큰 0번의 경우, 이 경우 130APS -> 96APS 로 속도가 줄어든다.

-s 를 넣어 디버깅정보를 털어낸 컴파일된 루아파일의 경우 130APS -> 100APS 정도가 된다. 

의외로... 컴파일된 루아파일의 실행속도가 오래전에 테스트 했을 때와 조금 다르다... 횟수를 여러번 돌려서 측정해봐야 할 듯...



속도를 APS (Action per Second) 단위로 측정하기 위해 내부에서 QueryPerformanceCounter() 를 호출하는 Lua.InitPerfValue(), Lua.GetPerfValue() 글루함수를 추가하고 다음 코드를 실행시켜 보았다. 

Lua.InitPerfValue();

j = 0;
for i=1, 10000000, 1 do
  j = j + i;
end;

print('------------');
print('Value', j);
print('APS', Lua.GetPerfValue());


결과는 다음과 같다. 먼저 LuaJIT 을 사용하지 않은 상태.




사용한 경우. 




단순한 루프의 경우, 약 3.3 배의 속도향상을 보인다. 


현재 맵서버는 여러 이벤트에서 컴파일된 루아파일을 숫자아이디로 구분해 호출하는 방식도 사용하고 있으므로, luaL_loadbuffer() 의 수행속도도 체크 해 보자. 현재 이 처리는 Lua.ExecLuaScript() 라는 글루함수가 처리하고 있다. 이 함수를 여러 번 반복호출하여 성능을 알아보자. 

Lua.InitPerfValue();

for i=1, 100, 1 do
  Lua.ExecLuaScript(0);
end;

print('------------');
print('APS', Lua.GetPerfValue());



컴파일 되지 않은 날 루아파일의 경우 

LuaJIT 비사용 : 1.37~1.40 APS.
LuaJIT 사용 :  1.13~1.14 APS


컴파일된 루아파일의 경우

LuaJIT 비사용 : 1.34~1.42 APS.
LuaJIT 사용 :  1.08~1.13 APS


-s 옵션으로 컴파일된 루아파일의 경우

LuaJIT 비사용 : 1.39~1.45 APS.
LuaJIT 사용 :  1.08~1.11 APS


LuaJIT 의 성능이 전반적으로 30% 가량 낮게 나오고, 오히려 컴파일된 경우 그 차이는 더욱 커지게 된다. 
기존에 적재된 함수를 사용하는 경우, 즉 플레이어의 밸런스 처리 등에서는 속도향상이 있지만, 이벤트에 따른 루아 호출에서는 오히려 속도감소가 발생할 수 있다는 이야기. 불러온 뒤 JIT처리를 하는 부분에 상당한 로드가 걸린다는 것을 알 수 있다. 

JIT 으로 처리된 상태를 그대로 저장할 수 있다면 속도를 올리는 데 많은 도움이 될텐데... 흠냐리... 











 

루아 디버깅 테스트

탐구생활/Lua
게임오븐을 보고... 며칠간 잠을 설쳤다. "디버깅이 되는구나... 디버깅이 되는구나... 디버깅이 되는구나... "
도대체 어떤 멋진 PM이 이런 계획을 세우고 또 어떤 멋진 프로그래머가 그 계획을 구체화 시켰을까...
진짜 부럽고 부럽고 부러웠다.

나도 저런 것 만들어보고 싶어~~ 라는 기분도 참 오래간만에 느껴보았다.
디버거만 있다면 우리 회사 기획자들을 내가 만든 루아지옥에서 건져올려 좀 더 가정에 충실하게 만들 수 있을텐데...

우리 게임의 경우 서버와 클라이언트의 글루함수 및 호출규약이 많이 다르다. 따라서 게임오븐처럼 통일된 IDE에서 클라이언트, 또는 서버를 구동시키는 것은 그다지 효율적이지 않다. 뭐 조금 바뀔 때 마다 툴도 손대야 하는 상황은 이제 정말 싫어... 한 번 만들어놓고 멀건 국물 나올 때 까지 울궈먹는 게 무엇보다 쵝오.

때문에 디버거를 루아 모듈로 만들어 기존 환경에서 구동시키는 쪽으로 살짝 생각을 바꿔보았다. 그래서 탄생한 물건이 바로 이 놈!!

dbgtest.zip



단독실행이 아닌 모듈이기 때문에 기존 루아환경에서 불러들여야 한다. 뭐 멀리서 찾을 것 없이 lua.org 에서 배포하는 윈32용 루아 바이너리를 내려받자.

압축을 풀고 dbgtest.dll 을 여기에 복사 해 넣자. 이제 lua5.1.exe 을 실행시키고 다음과 같이 디버그 패키지를 부른다.

> require "dbgtest"

다음 화면과 같이 출력되면 디버그 패키지가 정상적으로 불러들여졌다는 뜻이다.


이제 방금 불러들인 디버그 모듈의 유일한 글루함수인 show() 를 실행시켜 보자.

> dbgtest.show()

화면에 썰렁하기 이를 데 없는 디버깅 화면이 나타날 것이다.


단축키는 기본적으로 델파이의 IDE와 동일하다. 

  F5 : 브레이크 포인트 설정. (줄번호 부분 클릭) 
  F7 : 트레이스 인투. (미구현) 
  F8 : 트레이스 스텝. (Next 버튼)
  F9 : 시작, 또는 다음 브레이크 포인트 까지 실행. (Start, Continue 버튼)
  Ctrl + F2 : 실행 종료. 디버깅 정지. (Stop 버튼) 

적당한 곳에 브레이크 포인트를 걸어주고 "Start" 버튼을 눌러주자. 현재 스탭에서의 지역, 전역변수 및 설정된 와치값이 표시된다.

이 모듈은 태생적인 제약을 몇가지 가지고 있다.

1. 루아 5.1.4 이전버전을 지원하지 않는다.
2. 코루틴의 디버깅이 엉망이다.
3. 지역변수에 설정된 이중테이블값을 와치에서 읽을 수 없다.
4. 화면 구성이 허접하다.

하지만, 어차피 사내 기획자들이 코루틴을 주무르는 것도 아니고... 로컬에서 이중테이블도 어차피 로컬변수 창에서 확인 할 수 있으니 일단은 써먹을 수 있다!!

덧붙여, 루아 컴파일시 설정에 따라 와치가 동작하지 않을 가능성도 있다.
http://www.gpgstudy.com/forum/viewtopic.php?p=120049#120049  (류광님의 답변)

이 부분은 3번 문제도 해결할 겸, 좀 더 맑은정신으로 하루 날잡아 스택을 하나 하나 정성스럽게 옮기는 방식으로 코드를 바꿔보자.

 

달랑 디버거만 구동되면 뭔가 썰렁할 듯 해서 예제코드를 넣어두었습니다. 이 코드가 보기 싫으면 실행파일과 같은 위치에 빈 noname.lua 파일을 넣어두면 됩니다.




LNUM 패치가 적용된 arm-wince 용 루아 5.1.4

탐구생활/Lua

LNUM 패치가 적용된 루아 5.1.4 의 arm-wince 용 바이너리 이다.
컴파일러는 CeGCC 의 arm-mingw32ce 를 사용했다.

루아의 표준 라이브러리 중 io, os, loadlib 세 가지는 빼 버렸다.
따라서 io.open(), os.date(), package.loadlib() 등의 함수는 사용할 수 없다.
wince의 C런타임에 tmpfile() 같은 표준 C함수가 없기 때문에 이리저리 고민하다가
일단은 제거 빼 버렸는데, 사실 루아를 쓸 때 표준 라이브러리 의존도는 상대적으로
떨어지고 필요하면 글루함수로 만들어 추가하는 편이 나을 것이다.

(솔직히 루아는 테이블만 있으면~~~)


이제 이 라이브러리를 라자루스에 붙여볼까나~~~



 


2009.3.20
 
실수값에 float, 정수값에 int32 를 사용하는 버전.
luac.exe 를 사용해 컴파일한 바이트코드는 서로 다른 설정을 가진 루아 모듈 사이에서 호환되지 않는다.
따라서 동일한 설정으로 컴파일된 윈도32용 바이너리도 함께 넣어 두었다.

arm-wince 에는 표준 라이브러리 중 base, table, string, debug 만 포함되었다.
가급적 math는 집어넣으려 했지만, float모드로 사용할 때 발생한 atanhf 의 링크에러를
풀지 못했다. 따라서 필요하다면 글루함수로 만들어 제공해야 한다.