브로드캐스팅

브로드캐스팅 (Broadcasting)

브로드캐스팅은 Octave의 이항 연산자와 함수가 크기가 다른 행렬이나 배열 피연산자(인자)에 대해 어떻게 동작하는지를 말해요. 버전 3.6.0부터 Octave는 요소별 이항 연산자와 함수를 사용할 때 벡터·행렬·배열을 자동으로 브로드캐스트해요.

출처: 문서

본문

브로드캐스팅은 Octave의 이항 연산자와 함수가 크기가 다른 행렬이나 배열 피연산자(인자)를 만났을 때 어떻게 동작하는지를 설명해요. 버전 3.6.0부터 Octave는 요소별 이항 연산자와 함수를 사용할 때 벡터·행렬·배열을 자동으로 브로드캐스트해요. 대체로 더 작은 배열이 호환 가능한 모양을 가질 때까지 더 큰 배열을 가로질러 "브로드캐스트"돼요. 규칙은 배열의 대응 차원이 다음 중 하나여야 한다는 거예요.

  1. 같아야 한다.
  2. 또는 둘 중 하나가 1이어야 한다.

모든 차원이 같으면 브로드캐스팅은 일어나지 않고 일반적인 요소별 산술이 수행돼요. 더 높은 차원의 배열에서 차원 수가 같지 않다면, 빠진 뒤쪽 차원들은 1로 취급돼요. 어떤 차원이 1이면, 그 단일 차원(singleton dimension)을 가진 배열이 다른 배열의 차원과 일치할 때까지 그 차원을 따라 복사돼요. 예를 들어

x = [1 2 3;
     4 5 6;
     7 8 9];

y = [10 20 30];

x + y

브로드캐스팅이 없다면 x + y는 차원이 맞지 않아 오류예요. 그러나 브로드캐스팅이 있으면 마치 다음 연산을 수행한 것처럼 동작해요.

x = [1 2 3
     4 5 6
     7 8 9];

y = [10 20 30
     10 20 30
     10 20 30];

x + y
⇒     11   22   33
      14   25   36
      17   28   39

즉, 크기 [1 3]인 더 작은 배열이 단일 차원(행 수)을 따라 [3 3]이 될 때까지 복사돼요. 다만 실제 복사는 일어나지 않아요. 내부 구현은 메모리에 복사하지 않고 원하는 효과를 얻기 위해 필요한 차원을 따라 요소를 재사용해요.

두 배열이 서로를 가로질러 브로드캐스트될 수도 있어요. 예를 들어 벡터 자체와 모든 쌍별 차이:

y - y'
⇒     0   10   20
    -10    0   10
    -20  -10    0

여기서 크기 [1 3][3 1]인 벡터들이 일반 행렬 뺄셈이 일어나기 전에 둘 다 크기 [3 3]인 행렬로 브로드캐스트돼요.

브로드캐스팅의 특수한 경우로 익숙할 수 있는 것은, 브로드캐스트되는 배열의 모든 차원이 1일 때, 즉 그 배열이 스칼라일 때예요. 따라서 x - 42max (x, 2) 같은 연산은 브로드캐스팅의 기본 예시예요.

더 높은 차원의 예로, img가 크기 [m n 3]인 RGB 이미지이고 각 색상을 서로 다른 스칼라로 곱하고 싶다고 해보죠. 다음 코드는 브로드캐스팅으로 이를 수행해요.

img .*= permute ([0.8, 0.9, 1.2], [1, 3, 2]);

permute를 사용해 [0.8, 0.9, 1.2] 벡터의 차원을 img와 맞추는 것에 주목하세요.

브로드캐스팅 시맨틱스로 작성되지 않은 함수의 경우 bsxfun이 브로드캐스트하도록 강제하는 데 유용할 수 있어요.

C = bsxfun (f, A, B)

bsxfun은 이항 함수 f를 두 배열 인자 AB에 요소별로 적용하며, 필요에 따라 어느 입력 인자의 단일 차원이든 확장해요. f는 함수 핸들, 인라인 함수, 또는 평가할 함수 이름을 담은 문자열이에요. f는 같은 길이의 두 열 벡터 인자, 또는 열 벡터 인자 하나와 스칼라를 받을 수 있어야 해요. AB의 차원은 같거나 단일차원이어야 해요. 배열들의 단일 차원은 다른 배열과 같은 차원으로 확장돼요.

브로드캐스팅은 두 브로드캐스팅 조건 중 하나라도 성립할 때만 적용돼요. 하지만 평소와 마찬가지로 두 차원이 다르고 둘 다 1이 아닐 때는 브로드캐스팅이 적용되지 않아요.

x = [1 2 3
     4 5 6];
y = [10 20
     30 40];
x + y

이것은 비적합(nonconformant) 인자에 대한 오류를 만들어요.

일반 산술 연산 외에도 여러 두 인자 함수가 브로드캐스트해요. 브로드캐스트하는 함수와 연산자의 전체 목록은 다음과 같아요.

      plus      +
      minus     -
      times     .*
      rdivide   ./
      ldivide   .\
      power     .^
      lt        <
      le        <=
      gt        >
      ge        >=
      ne        !=  ~=
      and       &
      or        |
      atan2
      hypot
      max
      min
      mod
      rem
      xor

      +=  -=  .*=  ./=  .\=  .^=  &=  |=

브로드캐스팅의 힘을 보여주는 실제 예가 있어요. Floyd-Warshall 알고리즘은 그래프의 모든 정점 쌍 사이의 최단 경로 길이를 계산하는 데 사용돼요. 차수 n인 그래프 인접 행렬에 대한 순진한 구현은 이렇게 생겼을 거예요.

for k = 1:n
  for i = 1:n
    for j = 1:n
      dist(i,j) = min (dist(i,j), dist(i,k) + dist(k,j));
    endfor
  endfor
endfor

가장 안쪽 루프를 벡터화하면 이렇게 되죠.

for k = 1:n
  for i = 1:n
    dist(i,:) = min (dist(i,:), dist(i,k) + dist(k,:));
  endfor
endfor

양방향 브로드캐스팅을 사용하면 이렇게 돼요.

for k = 1:n
  dist = min (dist, dist(:,k) + dist(k,:));
endfor

정점 100개인 그래프에서 세 기법의 상대적 시간 성능은 순진한 코드가 7.3초, 단일 벡터화 코드가 87밀리초, 완전 브로드캐스트 코드가 1.3밀리초예요. 정점 1000개인 그래프에서는 벡터화가 11.7초인 반면 브로드캐스팅은 1.15초밖에 걸리지 않아요. 따라서 일반적으로 성능을 위해 브로드캐스팅 시맨틱스로 코드를 작성할 가치가 있어요.

하지만 더 단순한 연산으로 충분하다면 브로드캐스팅에 의존하지 말아야 해요. 행렬 ab에 대해 다음을 고려해 보세요.

c = sum (permute (a, [1, 3, 2]) .* permute (b, [3, 2, 1]), 3);

이 연산은 요소별 곱셈 중에 차원이 변환된 두 행렬을 서로를 가로질러 브로드캐스트해 더 큰 3차원 배열을 얻고, 그 배열을 세 번째 차원을 따라 합산해요. 잠시 생각해 보면 이 연산이 훨씬 빠른 일반 행렬 곱셈 c = a * b;과 같다는 걸 알 수 있어요.

용어에 대한 참고: "브로드캐스팅"은 Python 프로그래밍 언어의 Numpy 수치 환경이 유행시킨 용어예요. 다른 언어와 환경에서는 브로드캐스팅이 이진 단일 확장(BSX, MATLAB에서, 그리고 bsxfun 함수 이름의 유래), 재활용(recycling, R 프로그래밍 언어), 단일 명령 다중 데이터(SIMD), 또는 복제(replication)로도 알려져 있어요.

더 알아보기

  • 브로드캐스팅과 레거시 코드(Broadcasting and Legacy Code) 항목을 이어서 보면 좋아요.
  • arrayfun, cellfun, bsxfun 함수도 함께 살펴보세요.