2015년 4월 20일 월요일

[코로나SDK] sin, cos 연산시간 단축시키기

  어떤 좌표점을 회전시키고자 할 때 회전행렬를 곱해주는데 이 때 sin함수와 cos함수를 사용해야 한다. 예를 들어서 좌표점(x1, y1)을 원점을 중심으로 40도 회전시킨 좌표 (x2, y2)는 다음과 같은 식으로 구한다.

--------------------------------------------------------------------
          x2 = x1 cos(40도) - y1 sin(40도)
          y2 = x1 sin(40도) + y1 cos(40도)
--------------------------------------------------------------------

코로나(루아)에서 이 값들은 math.cos(rad) 와 math.sin(rad) 함수를 이용하여 계산할 수 있는데 입력값이 도(degree)가 아니라 라디안(radian)이다. 여러 가지 이유로 보통은 회전각을 도값으로 결정하는데 (예를 들어서 30도, 45도 등등) 이것의 sin, cos값을 계산하려면 이것에 π/180 으로 곱해서 라디안으로 환산시켜야 한다. 이를 위해서 다음과 같이 (변환)상수를 미리 계산해 놓고 sin(30도), cos(-45도)를 구할 수 있다.

--------------------------------------------------------------------
          local _d2r = math.pi / 180
          ...
          local alpha = math.sin(30*_d2r)
          local beta = math.cos(-45*_d2r)
--------------------------------------------------------------------

  그런데 이러한 회전 연산이 한 프레임 안에서 굉장히 많이 일어날 경우 조금이라도 연산시간을 줄이는 것이 유리할 것이다.가장 손쉬운 방법은 math.sin, math.cos함수를 로컬로 정의해서 호출하는 것이다. 예를 들어서 아래와 같이 정의해 놓고 사용하면 수행 시간이 조금이나마 단축된다.

--------------------------------------------------------------------
          local _d2r = math.pi / 180
          local Sin = math.sin
          local Cos = math.cos
          ...
          local alpha = Sin(30*_d2r)
          local beta = Cos(-45*_d2r)
--------------------------------------------------------------------

이렇게만 해도 (필자가 간단하게 실험해 본 결과로는) 15%정도 실행시간이 단축된다.

  시간을 좀 더 단축시킬 수 있는 방법으로 미리 이 값들을 계산해서 테이블(배열)에 저장하는 방법을 생각해 볼 수 있다. 특수한 경우가 아니면 보통 각도는 1도 단위로 많이 계산되므로 예를 들어 -360도부터 360도까지 (원하는 범위는 상황에 따라 다를 것이다.) 미리 계산해 놓는 것이다. 루아는 배열의 인덱스가 음수도 가능하므로 아래와 같이 하면 된다.

--------------------------------------------------------------------
          local _d2r = math.pi/180
          local tCos, tSin = {}, {}
          for deg=-360,360 do
                    tCos[deg]=math.cos(deg*_d2r)
                    tSin[deg]=math.sin(deg*_d2r)
          end
--------------------------------------------------------------------

이제 이렇게 미리 생성해 놓으면 이후에는 단순히 아래와 같이 배열값을 읽어오는 것만으로도 sin, cos계산이 가능하다.

--------------------------------------------------------------------
          local alpha = tSin[30]
          local beta = tCos[-45]
--------------------------------------------------------------------

필자가 테스트해 본 바로는 연산시간이 첫 번째의 경우보다 50%, 약 절반으로 줄어들었다. 이 방법의 단점은 실수각(예를 들어 30.5도 같은)의 연산이 불가하다는 것이고 약간의 메모리를 소비한다는 것이다.

  필자는 이 방법이 훨씬 빨라서 적어도 90%이상은 실행 시간이 감소될 것으로 예상했었는데 단축시간은 그다지 크지 않았다. 그 이유는 내장 수학함수는 미리 컴파일된 라이브러리가 실행되지만(빠르다) 인덱싱은 인터프리팅으로 실시간 수행되기 때문에(느리다) 그 차이가 그다지 크지 않은 것으로 짐작된다.

댓글 없음:

댓글 쓰기