이번 시간하고 다음 시간엔 애플리케이션 퍼포먼스 및 메모리 관리에 대해 다룹니다.
아직까지 모바일 기계는 작기 때문에 CPU나 Memory 에 한계가 많습니다. 이로인한 속도 저하나 에러방지를 위해 신경써야할 퍼포먼스 및 메모리 관리에 대해 알아봅니다.
Performance and Optimization
앱 개발 할 때 항상 앱의 퍼포먼스에 대해 신경을 써야 합니다. 특히 아직까지 모바일은 메모리나 배터리 성능에 많은 한계가 있습니다.
코로나에서는 garbage collection을 통해서 많은 부분을 자동으로 관리해 주고 있습니다.
코로나에서의 메모리 관리는 아래 다섯가지에 대해 고민 하면 됩니다.
1. Display objects
2. Global variables
3. Runtime Listeners
4. Timers
5. Transitions
메모리 낭비와 관련해서 체크할 방법이 있습니다.
아래 코드를 main.lua 파일 맨 아래에 넣으세요.
(main.lua말고 다른 파일에 넣어도 됩니다.)
local monitorMem = function()
collectgarbage()
print( "MemUsage: " .. collectgarbage("count") )
local textMem = system.getInfo( "textureMemoryUsed" ) / 1000000
print( "TexMem: " .. textMem )
end
Runtime:addEventListener( "enterFrame", monitorMem )
MemUsage는 Lua 메모리를 나타냅니다. 그리고 TexMem은 그래픽 등이 사용하는 메모리를 나타냅니다. 여기서 중요한 것은 숫자가 얼마나 크냐가 아니고 앱이 진행 되면서 이 숫자가 어떻게 변하느냐를 보시는 겁니다.
스크린 1에서 2로 갔을 때 메모리 변화와 다시 1로 왔을 때의 메모리 변화를 보실 필요가 있습니다. 계속 늘어나기만 하면 어디에선가 메모리 누수가 있다는 얘기입니다.
Display Objects
디스플레이 오브젝트와 관련된 메모리 누수를 막는 방법은 객체를 정리할 때 항상 nil을 대입하는 겁니다.
display.remove( redBall )
redBall = nil
혹시 루핑을 돌면서 계속 객체를 새로 만들 경우 객체 remove를 하지 못하는 경우가 있는데요. 개발자는 항상 객체의 탄생과 소멸까지 책임 져야 합니다.
Global Variables
객체 생성할 때 local 을 붙이지 않으면 그것은 글로벌 변수입니다. 가능한 글로벌 변수보다 로컬 변수를 사용하는 것이 메모리 누수를 막는 방법입니다. 만약 사용하더라도 더이상 그 변수가 필요하지 않으면 반드시 nil 처리를 합니다. 그러면 루아의 garbage collector가 이것을 정리할 수 있습니다.
Runtime Listeners
display object를 remove할 때 이 객체에 붙어 있는 listener도 메모리에서 함께 해제 됩니다. Runtime에 리스너를 달게 되면 개발자가 직접 그 리스너를 remove할 때까지 메모리 해제 되지 않습니다. Runtime/enterFrame 리스너에서 일반적으로 일어나는 메모리 누수는 어느 한 screen에 Runtime 리스너가 달렸는데 그 스크린을 떠날때 이 리스너를 없애지 않는 경우 입니다. 이 경우 다음에 다시 이 스크린으로 돌아오게 되면 Runtime 리스너가 두개가 되기 때문입니다.
Timers and Transitions
Timer와 Transition이 일반적으로 가장 crash를 많이 유발할 개연성이 있습니다. 예를 들어 time 하고 timer/transition start 사이에 end가 와 버리면 충돌 하게 됩니다. 또한 이것을 nil 처리 하지 않으며 이것은 메모리에 계속 남아 있으면서 좀 더 많은 메모리 누수를 유발합니다.
이것을 관리하는 효율적인 방법은 모든 timer와 transition들을 하나의 테이블(배열)로 관리하는 겁니다. 그러면 더이상 아무런 타이머와 트랜지션이 필요 하지 않을 때 한번에 nil처리 할 수 있습니다.
timerStash = {}
transitionStash = {}
function cancelAllTimers()
local k, v
for k,v in pairs(timerStash) do
timer.cancel( v )
v = nil; k = nil
end
timerStash = nil
timerStash = {}
end
--
function cancelAllTransitions()
local k, v
for k,v in pairs(transitionStash) do
transition.cancel( v )
v = nil; k = nil
end
transitionStash = nil
transitionStash = {}
end
아래는 timer와 transition 을 생성할 때의 예 입니다.
timerStash.newTimer = timer.performWithDelay( ...
transitionStash.newTransition = transition.to( myObject { ...
만약 cancel하지 말아야 될 타이머나 트랜지션은 이 테이블에 포함 시키지 않을 수 있습니다.
효과적으로 메모리 사용하기
- 메모리 누수 방지하기
- 가능한 리소스 파일 크기를 작게 하기
- 필요한 시점에 리소스 로드하기
- display object가 필요하지 않으면 display hierarchy 에서 remove 하기
나쁜 예)
-- rect is a global variable
rect = display.newRect(0,0,10,10)
rect:setFillColor(255,255,255)
local function removeOnTap( event )
local t = event.target
local parent = t.parent
-- remove from display hierarchy
-- but var "rect" still exists
-- so memory is never freed
parent:remove( t )
return true
end
rect:addEventListener( "tap", removeOnTap )
좋은 예)
-- rect is a local variable
local rect = display.newRect(0,0,10,10)
rect:setFillColor(255,255,255)
local function removeOnTap( event )
local t = event.target
local parent = t.parent
-- remove from display hierarchy
parent:remove( t )
return true
end
rect:addEventListener( "tap", removeOnTap )
위의 나쁜 예는 rect가 글로벌 변수로 선언이 돼 있기 때문에 parent:remove(t)를 해도 rect는 메모리에서 해제되지 않습니다. 좋은 예처럼 변수를 local로 처리하면 메모리가 깨끗하게 정리 됩니다.
Reducing Power Consumption (파워 소비 줄이기)
모바일 디바이스는 배터리 생명이 짧습니다. 개발자는 배터리 소비를 줄이기 위해 아래와 같은 사항들을 되도록 안 쓰도록 신경을 쓸 수 있습니다.
- Network traffic (Wi-Fi radios and baseband cell radios)
- GPS
- Accelerometers
- Disk accesses (reading/writing to files)
Network
위 요소들 중 네트워크 access가 가장 파워를 많이 사용합니다. 될 수 있는 대로 네트워크 트래픽을 줄여야 합니다.
아래 사항들을 참고 하세요.
- 필요할 때만 외부 네트워크 연결을 한다.
- 가능한 전달할 데이터의 사이즈를 최소화 한다.
- Transmit in bursts. More power is consumed the longer the radio is actively transmitting data. Therefore, transmit the data in bursts instead of spreading out the same data into smaller transmission packets over time.
- 위치 데이터는 GPS,cell, Wi-Fi 네트워크를 통해 얻어 집니다. 이 위치 데이터도 필요한 경우만 사용 합니다.
Graphics
Group objects
Turn off animations for non-visible objects
애니메이션이 non-visible이 될 경우 또는 그 객체가 스크린 밖에 위치할 때에는 가능한한 그 애니메이션을 멈춥니다.
Optimize image size
큰 이미지는 로드 할 때 더 많은 시간과 메모리를 소비합니다.
이 큰 이미지가 더 이상 필요하지 않으면 parent group에서 이 객체를 remove합니다.
local image = display.newImage( "image.png" )
-- do stuff with image
image:getParent():remove( image )
Lua: Best Practice
로컬 변수를 사용하라
글로벌 변수
for i = 1, 1000000 do
local x = math.sin(i)
end
로컬 변수
local sin = math.sin
for i = 1, 1000000 do
local x = sin(i)
end
특히 위와 같이 긴 루프를 사용 할 경우 로컬로 변수를 선언하면 훨씬 가볍습니다. 위 코드를 예로 들면 글로벌 변수 예제는 로컬 변수 예제보다 30% 더 실행 시간이 느립니다.
함수를 사용할 경우에 함수 밖에서 로컬로 변수를 선언 하면 글로벌 변수보다 속도가 빠릅니다.
글로벌
function foo (x)
for i = 1, 1000000 do
x = x + math.sin(i)
end
return x
end
External local
local sin = math.sin
function foo (x)
for i = 1, 1000000 do
x = x + sin(i)
end
return x
end
이 경우에도 글로벌 변수를 사용한 코드가 30% 더 속도가 느립니다.
수학
x*0.5 는 x/2 보다 빠릅니다.
x*x 는 x^2보다 빠릅니다.
배열에 객체 집어 넣기
t[#t+1] = item은 table.insert(t,item)보다 빠릅니다.
Constant Folding
i=111+111은 i=222 와 속도가 같습니다. 왜냐하면 컴파일러는 그 값을 미리 계산하기 때문입니다.
1+2+x 는 (1+2) + x 로 간주 되서 3+x와 같은 시간이 걸립니다.
하지만 x+1+2는 (x+1)+2로 간주 되서 미리 계산하지 않아서 시간이 더 걸리게 됩니다.
Cache properties in a local variable
어떤 값이 계속 사용된다면 Cashe에 올리는 것이 속도가 빠릅니다.
Uncached
function foo (o)
local n = 0
for i = 1, 1000000 do
-- lookup o.x each time
n = i + n * o.x
end
return n
end
Cached
function foo (o)
-- cache o.x before the loop
local x = o.x
local n = 0
for i = 1, 1000000 do
n = i + n * x
end
return n
end
다음 시간에도 계속해서 여러 프로그래밍 테크닉에 대해서 알아 보겠습니다.
'Corona SDK > Corona Doc' 카테고리의 다른 글
CoronaSDK 2011.715 버전 Email, SMS 기능 추가 (0) | 2012.01.04 |
---|---|
화면 전환 Storyboard API 개요 (0) | 2011.12.10 |
Corona SDK 프로그래밍 테크닉 4 - Corona DOC 마지막 강좌 - (4) | 2011.11.11 |
Corona SDK 프로그래밍 테크닉 3 (0) | 2011.11.10 |
Corona SDK 프로그래밍 테크닉 2 (2) | 2011.11.09 |
Corona SDK Native UI (0) | 2011.11.07 |
Location and Maps (0) | 2011.11.04 |
이벤트, 리스너로 다이나믹한 표현 하기 3 (4) | 2011.11.03 |
이벤트, 리스너로 다이나믹한 표현 하기 2 (4) | 2011.11.02 |
이벤트, 리스너로 다이나믹한 표현 하기 1 (0) | 2011.11.01 |