Main Content

이 번역 페이지는 최신 내용을 담고 있지 않습니다. 최신 내용을 영문으로 보려면 여기를 클릭하십시오.

중첩 parfor 루프와 중첩 for 루프 및 기타 parfor 요구 사항

중첩 parfor 루프

parfor 루프를 또다른 parfor 루프 내에 사용할 수 없습니다. 예를 들어, 다음과 같은 parfor 루프의 중첩은 허용되지 않습니다.

parfor i = 1:10
    parfor j = 1:5
        ...
    end
end

parfor를 다른 parfor 루프 내에 직접 중첩할 수 없습니다. parfor 루프는 parfor 루프가 포함된 함수를 호출할 수 있지만 이에 대해 추가적으로 병렬 처리는 수행하지 않습니다.

MATLAB® 편집기의 코드 분석기는 parfor를 또다른 parfor 루프 내에 사용하면 플래그를 지정합니다.

Code analyzer warning stating that PARFOR or SMPD cannot be used inside another PARFOR-loop.

병렬화는 하나의 수준에서만 수행할 수 있으므로 parfor 루프를 중첩할 수 없습니다. 따라서 병렬로 실행할 루프를 선택하고 다른 루프는 for 루프로 변환하십시오.

중첩 루프를 처리할 때는 다음 성능 관련 사항을 고려하십시오.

  • 병렬 처리는 오버헤드를 발생시킵니다. 일반적으로 바깥쪽 루프를 병렬로 실행해야 하는데, 이렇게 하면 오버헤드가 한 번만 발생하기 때문입니다. 안쪽 루프를 병렬로 실행하면 여러 개의 parfor를 실행할 때마다 오버헤드가 발생합니다. 병렬 연산에 드는 오베헤드를 측정하는 방법의 예제는 중첩 for 루프를 parfor 루프로 변환하기 항목을 참조하십시오.

  • 반복 횟수가 워커 개수보다 많도록 하십시오. 그렇지 않으면 사용 가능한 모든 워커를 사용하지 않습니다.

  • parfor 루프 반복 시간의 균형을 맞춰 보십시오. parfor는 일부 부하 불균형을 보정합니다.

항상 가장 바깥쪽 루프를 병렬로 실행해야 병렬 연산에 드는 오버헤드를 줄일 수 있습니다.

또한 parfor를 사용하는 함수도 사용할 수 있으며 이 함수를 parfor 루프에 포함할 수도 있습니다. 병렬화는 바깥쪽 수준에서만 발생합니다. 다음 예제에서는 바깥쪽 parfor 루프 내에서 MyFun.m 함수를 호출합니다. MyFun.m에 포함된 안쪽 parfor 루프는 병렬이 아닌 순차적으로 실행됩니다.

parfor i = 1:10
    MyFun(i)
end

function MyFun(i)
    parfor j = 1:5
        ...
    end
end

중첩 parfor 루프는 일반적으로 계산적인 이점이 없습니다.

중첩 for 루프를 parfor 루프로 변환하기

중첩 루프의 일반적인 사용은 하나의 루프 변수를 사용하여 하나의 차원을 인덱싱하고 중첩 루프 변수를 사용하여 다른 차원을 인덱싱하여 배열을 순차적으로 실행하는 것입니다. 기본 형식은 다음과 같습니다.

X = zeros(n,m);
for a = 1:n
    for b = 1:m
        X(a,b) = fun(a,b)
    end
end

다음 코드는 간단한 예제를 보여줍니다. tictoc을 사용하여 필요한 계산 시간을 측정합니다.

A = 100;
tic
for i = 1:100
    for j = 1:100
        a(i,j) = max(abs(eig(rand(A))));
    end
end
toc
Elapsed time is 49.376732 seconds.

중첩 루프 중 어느 하나를 병렬화할 수 있지만 둘 다 병렬로 실행할 수는 없습니다. 그 이유는 병렬 풀의 워커는 추가적으로 다른 병렬 풀을 시작하거나 액세스할 수 없기 때문입니다.

i로 세어지는 루프가 parfor 루프로 변환되면 풀의 각 워커는 j 루프 카운터를 사용하여 중첩 루프를 실행합니다. j 루프 자체는 각 워커에서 parfor로 실행할 수 없습니다.

병렬 처리는 오버헤드를 발생시키므로 안쪽 또는 바깥쪽 for 루프를 parfor 루프로 변환할지 신중하게 선택해야 합니다. 다음 예제에서는 병렬 연산에 드는 오버헤드를 측정하는 방법을 보여줍니다.

먼저 바깥쪽 for 루프만 parfor 루프로 변환합니다. tictoc을 사용하여 필요한 계산 시간을 측정합니다. 병렬 풀의 워커 간 데이터 전송량을 측정하려면 ticBytestocBytes를 사용합니다.

새 코드를 실행하고 다시 실행합니다. 병렬 풀을 시작하고 코드를 워커에서 사용 가능하도록 하는데 약간의 시간이 걸리기 때문에 첫 번째 실행이 이후의 실행보다 느립니다.

A = 100;
tic
ticBytes(gcp);
parfor i = 1:100
    for j = 1:100
        a(i,j) = max(abs(eig(rand(A))));
    end
end
tocBytes(gcp)
toc
             BytesSentToWorkers    BytesReceivedFromWorkers
             __________________    ________________________

    1             32984                 24512              
    2             33784                 25312              
    3             33784                 25312              
    4             34584                 26112              
    Total    1.3514e+05            1.0125e+05              

Elapsed time is 14.130674 seconds.

다음은 안쪽 루프만 parfor 루프로 변환합니다. 이전과 마찬가지로 필요한 시간과 전송되는 데이터를 측정합니다.

A = 100;
tic
ticBytes(gcp);
for i = 1:100
    parfor j = 1:100
        a(i,j) = max(abs(eig(rand(A))));
    end
end
tocBytes(gcp)
toc
             BytesSentToWorkers    BytesReceivedFromWorkers
             __________________    ________________________

    1        1.3496e+06             5.487e+05              
    2        1.3496e+06            5.4858e+05              
    3        1.3677e+06            5.6034e+05              
    4        1.3476e+06            5.4717e+05              
    Total    5.4144e+06            2.2048e+06              

Elapsed time is 48.631737 seconds.

안쪽 루프를 parfor 루프로 변환할 경우 데이터 전송량과 시간 모두 바깥쪽 루프를 병렬 처리할 때보다 훨씬 큽니다. 이 경우 경과 시간은 중첩 for 루프 예제에서와 거의 동일합니다. 바깥쪽 루프를 병렬로 실행하는 것보다 데이터 전송이 더 많고 따라서 병렬 연산에 드는 오버헤드도 더 많으므로 속도 향상 폭은 더 적습니다. 그러므로 안쪽 루프를 병렬로 실행하는 경우 일련의 for 루프를 실행하는 것에 비해 계산적으로는 이점이 없습니다.

병렬 연산에 드는 오버헤드를 줄이고 계산 속도를 높이려면 바깥쪽 루프를 병렬로 실행하십시오.

대신 안쪽 루프를 변환하는 경우에는 바깥쪽 루프를 반복할 때마다 별도의 parfor 루프를 시작합니다. 즉, 안쪽 루프 변환은 100개의 parfor 루프를 생성합니다. parfor를 실행할 때마다 오버헤드가 발생합니다. 병렬 연산에 드는 오버헤드를 줄이려면 대신 바깥쪽 루프를 병렬로 실행해야 합니다. 오버헤드가 한 번만 발생하기 때문입니다.

코드 속도를 높이려면 항상 바깥쪽 루프를 병렬로 실행하십시오. 병렬 연산에 드는 오버헤드가 감소하기 때문입니다.

중첩 for 루프: 요구 사항 및 제한 사항

중첩 for 루프를 parfor 루프로 변환하려면 루프 변수가 적절하게 분류되었음을 확인해야 합니다. parfor 루프에서 변수 문제 해결하기 항목을 참조하십시오. 코드가 필수라고 레이블이 지정된 지침과 제한 사항을 따르지 않을 경우 오류가 발생합니다. MATLAB은 코드를 읽어올 때 이러한 오류 중 일부를 포착합니다. 이러한 오류는 필수(정적)라는 레이블로 지정됩니다.

필수(정적): parfor 루프 안에 중첩되는 for 루프의 범위는 상수 또는 브로드캐스트 변수로 정의해야 합니다.

다음 예제에서 왼쪽의 코드는 함수 호출로 for 루프의 상한을 정의하므로 작동하지 않습니다. 오른쪽의 코드는 parfor 루프 바깥쪽에 브로드캐스트 변수나 상수 변수를 먼저 정의함으로써 우회적 해결 방법을 제공합니다.

유효하지 않음유효함
A = zeros(100, 200);
parfor i = 1:size(A, 1)
    for j = 1:size(A, 2)
        A(i, j) = i + j;
    end
end
A = zeros(100, 200);
n = size(A, 2);
parfor i = 1:size(A,1)
    for j = 1:n
        A(i, j) = i + j;
    end
end
필수(정적): 중첩 for 루프의 인덱스 변수는 for 문에 의해서만 명시적으로 할당되어야 합니다.

다음 제한 사항을 반드시 따라야 합니다. for 문을 사용해서가 아니라 parfor 루프 내의 임의의 위치에서 중첩 for 루프 변수를 변경하는 경우, for 루프 변수로 인덱싱하는 영역을 각 워커에서 사용할 수 있다고 보장되지 않습니다.

왼쪽의 코드는 루프 본문의 중첩 for 루프 변수 j의 값을 수정하려고 시도하기 때문에 유효하지 않습니다. 오른쪽의 코드는 중첩 for 루프 변수를 임시 변수 t에 할당한 다음 t를 업데이트하는 식으로 우회적 해결 방법을 제공합니다.

유효하지 않음유효함
A = zeros(10); 
parfor i = 1:10 
    for j = 1:10 
        A(i, j) = 1; 
        j = j+1; 
    end
end
A = zeros(10); 
parfor i = 1:10 
    for j = 1:10 
        A(i, j) = 1;  
        t = j;
        t = t + 1;
    end
end
필수(정적): 중첩 for 루프 변수를 인덱싱하거나 아래 첨자를 사용할 수 없습니다.

다음 제한 사항을 반드시 따라야 합니다. 중첩 for 루프 변수를 인덱싱하면 독립적인 반복이 보장되지 않습니다.

왼쪽의 예제는 중첩 for 루프 변수 j의 요소를 참조하려고 시도하기 때문에 유효하지 않습니다. 오른쪽의 예제는 이러한 요소 참조를 제거합니다.

유효하지 않음유효함
A = zeros(10); 
parfor i = 1:10 
    for j = 1:10 
        j(1);
    end
end
A = zeros(10); 
parfor i = 1:10 
    for j = 1:10 
        j;
    end
end
필수(정적): 중첩 for 루프 변수를 슬라이스 배열의 요소를 참조할 때 사용할 때 변수를 표현식의 일부가 아닌 일반 형식으로 사용해야 합니다.

예를 들어, 다음 왼쪽의 코드는 작동하지 않지만 오른쪽의 코드는 작동합니다.

유효하지 않음유효함
A = zeros(4, 11);
parfor i = 1:4
    for j = 1:10
        A(i, j + 1) = i + j;
    end
end
A = zeros(4, 11);
parfor i = 1:4
    for j = 2:11
        A(i, j) = i + j - 1;
    end
end
필수(정적): 중첩 for 루프를 사용하여 슬라이스 배열의 요소를 참조하는 경우 parfor 루프 내 다른 곳에서는 이 배열을 사용할 수 없습니다.

다음 예제에서 왼쪽의 코드는 A가 중첩 for 루프 내에서 나눠지고 요소 참조되기 때문에 작동하지 않습니다. 오른쪽의 코드는 v가 중첩 루프 바깥쪽에서 A에 할당되기 때문에 작동합니다.

유효하지 않음유효함
A = zeros(4, 10);
parfor i = 1:4
    for j = 1:10
        A(i, j) = i + j;
    end
    disp(A(i, j))
end
A = zeros(4, 10);
parfor i = 1:4
    v = zeros(1, 10);
    for j = 1:10
        v(j) = i + j;
    end
    disp(v(j))
    A(i, :) = v;
end

parfor 루프 제한 사항

중첩 함수

parfor 루프의 본문은 중첩 함수를 참조할 수 없습니다. 그러나 함수 핸들로 중첩 함수를 호출할 수는 있습니다. 다음 예제를 참조하십시오. parfor 루프의 A(idx) = nfcn(idx)는 작동하지 않습니다. feval을 사용하여 parfor 루프 본문에서 fcn 핸들을 호출해야 합니다.

function A = pfeg
    function out = nfcn(in)
        out = 1 + in;
    end
    
    fcn = @nfcn;
    
    parfor idx = 1:10
        A(idx) = feval(fcn, idx);
    end
end
>> pfeg
Starting parallel pool (parpool) using the 'Processes' profile ... connected to 4 workers.

ans =

     2     3     4     5     6     7     8     9    10    11

parfor 루프 내에서 중첩 함수를 참조하는 함수 핸들을 사용하는 경우에는 외부 범위 변수 값이 워커 간에 동기화되지 않습니다.

중첩 parfor 루프

parfor 루프의 본문은 parfor 루프를 포함할 수 없습니다. 자세한 내용은 중첩 parfor 루프 항목을 참조하십시오.

중첩 spmd

parfor 루프의 본문은 spmd 문을 포함할 수 없으며 spmd 문은 parfor 루프를 포함할 수 없습니다. 그 이유는 워커가 추가적으로 다른 병렬 풀을 시작하거나 액세스할 수 없기 때문입니다.

break 문 및 return

parfor 루프의 본문은 break 문이나 return 문을 포함할 수 없습니다. 대신 parfeval이나 parfevalOnAll에는 cancel을 사용할 수 있으므로 이 두 함수를 고려하십시오.

전역 변수 및 영속 변수

parfor 루프의 본문은 global 변수 선언이나 persistent 변수 선언을 포함할 수 없습니다. 그 이유는 이러한 변수가 워커 간에 동기화되지 않기 때문입니다. 함수 내에 global 변수나 persistent 변수를 사용할 수 있지만 변수의 값은 변수를 생성하는 워커에만 표시됩니다. 값을 공유하려면 global 변수 대신 함수 인수를 사용하는 것이 좋습니다.

변수 요구 사항에 대한 자세한 내용은 parfor 루프에서 변수 문제 해결하기 항목을 참조하십시오.

스크립트

스크립트에 새로운 변수가 있는 경우 parfor 루프나 spmd 문 내에서 이 스크립트를 호출할 수 없습니다. 그 이유는 이 스크립트가 투명성 위반을 야기하기 때문입니다. 자세한 내용은 parfor 루프 또는 spmd 문에서 투명성 확보하기 항목을 참조하십시오.

익명 함수

parfor 루프의 본문 내에 익명 함수를 정의하는 것은 허용됩니다. 그러나 익명 함수 내에 슬라이스 출력 변수는 허용되지 않습니다. 다음 예제에 보이는 대로 슬라이스 변수에 대한 임시 변수를 사용하여 이 문제를 해결할 수 있습니다.

x = 1:10;
parfor i=1:10
    temp = x(i);
    anonymousFunction = @() 2*temp;
    x(i) = anonymousFunction() + i;
end
disp(x);

슬라이스 변수에 대한 자세한 내용은 슬라이스 변수 항목을 참조하십시오.

inputname 함수

parfor 루프 내에서는 inputname을 사용하여 인수 번호에 해당하는 작업 공간 변수 이름을 반환하는 것이 지원되지 않습니다. 그 이유는 parfor 워커는 MATLAB 데스크탑의 작업 공간에 액세스할 수 없기 때문입니다. 이 문제를 해결하려면 다음 예제에 보이는 대로 parfor 전에 inputname을 호출하십시오.

a = 'a';
myFunction(a)

function X = myFunction(a)
    name = inputname(1);
    
    parfor i=1:2
        X(i).(name) = i;
    end
end

load 함수

출력 구조체에 할당하지 않는 load 구문은 parfor 루프 내에서 지원되지 않습니다. parfor 내에서는 항상 load의 출력값을 구조체에 대입하십시오.

nargin 함수 또는 nargout 함수

다음과 같이 사용하는 경우는 parfor 루프 내에서 지원되지 않습니다.

  • 함수 인수 없이 nargin 또는 nargout 사용

  • 현재 실행 중인 함수에 대한 호출의 입력 인수 개수 또는 출력 인수 개수를 확인하기 위해 narginchk 또는 nargoutchk 사용

그 이유는 워커가 MATLAB 데스크탑의 작업 공간에 액세스할 수 없기 때문입니다. 이 문제를 해결하려면 다음 예제에 보이는 대로 parfor 앞에 이러한 함수를 호출하십시오.

myFunction('a','b')

function X = myFunction(a,b)
    nin = nargin;
    parfor i=1:2
        X(i) = i*nin;
    end
end

P 코드 스크립트

parfor 루프 내에서 P 코드 스크립트 파일을 호출할 수 있지만 P 코드 스크립트는 parfor 루프를 포함할 수 없습니다. 이 문제를 해결하려면 P 코드 스크립트 대신 P 코드 함수를 사용하십시오.

참고 항목

| |

관련 항목