IDL/Programming

배열간 연산과 반복형 구문에 의한 연산 사이의 비교 [2]

이상우_IDL 2026. 2. 26. 15:42
728x90

지난 회차 게시물에서는 배열들을 사용하여 연산 결과를 얻는 방식에 있어서 배열간 연산과 반복형 구문에 의한 연산이라는 두가지 방식에 대하여 살펴보고 서로 비교해보았습니다. 특히 대용량 데이터를 다루게 될 경우에는 두 방식의 소요 시간이 서로 상당히 큰 차이를 보인다는 것도 함께 확인한 바 있습니다. 그러면 이번 회차에는 이러한 차이를 가늠할 수 있는 다른 예제를 살펴보기로 하겠습니다. 이번 예제에서는 반복형 구문 내에서 IF와 같은 조건형 구문이 사용되는 경우입니다. 예제를 보면 다음과 같습니다.

 

n = 10000000
a = RANDOMU(-1, n)*100
b = RANDOMU(-2, n)*100
c = RANDOMU(-3, n)*100

 

TIC
r = FLTARR(n)
FOR j = 0, n-1 DO BEGIN
  IF a[j] GE 50 THEN r[j] = (a[j]-40)/SQRT(b[j]+c[j])
  IF a[j] LT 50 THEN r[j] = (a[j]+10)/SQRT(b[j]+c[j])
ENDFOR
TOC

 

이 예제에서는 반복형 구문 내에서 IF문을 사용하여 특정한 조건에 따라 서로 다른 유형의 연산이 수행되도록 하였습니다. 실제로 이와 같은 방식으로 작업을 하게 되는 경우들이 종종 있습니다. 결론부터 얘기하면 이러한 방식으로 처리하면 시간은 많이 걸립니다. 즉 반복형 구문이라는 것만으로도 어느 정도 시간이 걸리게 되는데, 거기다가 그 안에서 조건형 구문까지 처리해야 한다면 이로 인한 시간이 더해지게 됩니다. 제 PC의 IDL에서 위의 내용을 실행해본 결과는 다음과 같습니다.

 

% Time elapsed: 1.9200001 seconds.

 

이와 같이 약 1.92초 정도의 시간이 걸리는 것으로 측정되었습니다. 물론 이러한 수치는 실행할 때마다 다를 수 있고 여러분의 PC에서도 다르게 나올 수 있습니다. 하지만 그리 적은 시간은 아닙니다. 그리고 여기서는 연산의 결과값들을 담은 배열 r을 얻었는데, 확인을 위하여 이 배열의 처음 10개의 값들만 출력해보면 다음과 같습니다.

 

FOR j = 0, 9 DO PRINT, r[j]

 

      1.92828
      2.56979
      4.68861
      1.63488
      3.24914
     0.964956
      3.58475
      3.35747
      1.19523
      3.71283

 

그런데 이러한 반복형 구문에 의한 연산보다 배열간 연산이 더 빠르다는 것은 이미 지난 회차 게시물에서 확인한 바 있습니다. 다만 위와 같이 배열 내의 값에 따라서 조건부로 서로 다른 연산을 수행해야 하는 경우라면 작업자의 입장에서는 배열간 연산의 방식으로 처리하기가 어렵지않을까 하는 생각이 들 수도 있고, 그러한 이유로 그냥 저렇게 반복형 구문으로 처리를 하게 되는 경우도 꽤 있을 것입니다. 물론 그렇게 해도 큰 문제는 없습니다. 시간이 어느 정도 더 걸릴 수도 있지만 그 차이가 아주 크지만 않다면 그냥 위와 같이 처리해도 무방합니다. 하지만 데이터의 양이 많고 처리할 연산 작업도 더 복잡한 경우라면 얘기가 달라질 수 있습니다. 이런 경우에는 작업의 효율과 소요 시간에 대한 고려를 해야합니다. 따라서 위와 같은 작업을 배열간 연산으로 처리할 수 있다면 더 좋을 것 같은데요. 위와 같이 배열 내 값에 따라 조건부로 연산의 내용이 달라지는 작업을 배열간 연산으로 처리하는 것도 가능합니다. 즉 위의 내용을 배열간 연산의 방식으로 변경한다면 그 내용은 다음과 같습니다.

 

TIC
r = FLTARR(n)
w1 = WHERE(a GE 50)
r[w1] = (a[w1]-40)/SQRT(b[w1]+c[w1])
w2 = WHERE(a LT 50)
r[w2] = (a[w2]+10)/SQRT(b[w2]+c[w2])
TOC

 

여기서 가장 주목할 부분은 WHERE 함수를 사용한 것입니다. 즉 앞서 반복형 구문에 의한 작업에서는 IF 구문을 사용하여 배열 a의 값에 따라 연산 과정이 다르게 적용된 바 있는데, 여기서는 배열 a의 값에 대한 판단을 WHERE 함수로 미리 해두고 그러한 조건에 맞는 경우들에 대해서만 배열간 연산으로 처리한 것입니다. 실제로 이와 같은 형태로 처리 방식을 변경해도 결과는 동일하게 나옵니다. 앞서 해봤던 것처럼 배열 r의 값들을 출력해보면 확인할 수 있습니다. 그리고 실행에 걸린 시간을 보면 다음과 같습니다.

 

% Time elapsed: 0.088999987 seconds.

 

이와 같이 약 0.089초 정도가 걸리는 것으로 나옵니다. 앞서 반복형 구문에 의한 연산보다 훨씬 빠르다는 것을 알 수 있습니다. 배수로는 약 21배 정도입니다. 물론 배열의 크기, 연산의 복잡성 등에 따라서는 이러한 차이가 더 크게 나타날 수도 있습니다. 어쨌든 이러한 결과를 본다면, 우리가 대용량 배열 데이터를 처리하는 작업을 해야 할 경우에는 가급적이면 반복형 구문에 의한 연산보다는 배열간 연산으로 처리하는 방향으로 코딩을 하는 것이 더 효율적이라는 것을 알 수 있습니다. 물론 작업의 특성에 따라서는 이게 어려운 경우도 있습니다. 그러한 예제를 보면 다음과 같습니다.

 

TIC
r = FLTARR(n)
FOR j = 0, n-1 DO BEGIN
  tmp = a[j]/SQRT(b[j]+c[j])+RANDOMU(seed)*10
  IF tmp LT 50 THEN r[j] = a[j]/SQRT(b[j]+c[j])+5 ELSE r[j] = a[j]/SQRT(b[j]+c[j])-5
ENDFOR
TOC

 

이 내용을 보면 반복형 구문의 내부에서 임시로 계산된 tmp의 값에 따라서 연산의 과정이 다르게 수행됩니다. 그런데 tmp의 계산 과정을 보면 RANDOMU 함수가 사용되었는데 이렇게 하면 예측할 수 없는 난수값이 생성되기 때문에 사전에 미리 배열화를 해두기가 어렵습니다. 물론 예제로 제시하기 위하여 제가 인위적으로 약간 어거지성 연산을 가정한 것이긴 합니다. 어쨌든 위의 내용을 실행해보면 예상대로 꽤 많은 시간이 걸리는 것으로 나옵니다.

 

% Time elapsed: 3.2830000 seconds.

 

이와 같이 약 3.28초 정도가 나오는데 앞서 했던 연산들에 비해서는 상당히 긴 시간입니다. 따라서 배열간 연산으로 바꿀 수만 있다면 그렇게 하는 것이 좋겠지만, 실전에서는 작업의 특성에 따라서는 위와 같이 어쩔 수 없이 반복형 구문을 사용해야만 하는 경우도 분명히 존재합니다.

 

어쨌든 중요한 것은 대용량의 데이터를 담은 배열들에 대한 연산을 수행하는데 있어서는 가급적이면 배열간 연산으로 처리하는 것이 좋다는 것입니다. 물론 작업의 특성에 따라 어쩔 수 없이 반복형 구문을 사용해야 하는 경우들도 있겠지만, 그런 경우들을 제외한 나머지 케이스들에 대해서는 배열간 연산으로 수행될 수 있도록 하는 것이 가장 효율적이라는 것을 염두에 두는 것이 좋습니다.

 

 

 

* 이 글이 도움이 되었다면 게시물에 대하여 공감 버튼(하트 모양) 클릭 및 블로그 구독도 해주시면 더 큰 힘이 됩니다. 감사합니다.

LIST