<Matlab 속도 개선을 위한 Parfor>
Ref : 나 (최형욱)
Script 기반의 Matlab은 쓰기 편한데(특히 행렬계열) 엄청난 단점이 존재한다. 바로 for 문이 상상 이상으로 느리다는점!! vetor 기반으로 연산을 하면 보완이 되긴 하지만 vector로 연산하기가 곤란하거나 매우 복잡해 지는 경우가 생긴다.
그럼 어쩌지? 바로 2속도 개선에 두가지가 있는데 1. coder를 이용하여 mex로 변환된 파일(C++ 최고!)을 사용하거나 아니면 2. 병렬처리(parfor)를 해주면 된다.
Parfor 를 사용하는게 간단하지만 의외로 에러가 났을 때 괜찮게 설명된 글을 찾기가 어려워서 몇가지를 정리한다.
1.사용가능성 테스트 2. 사용방법, 3. 몇가지 에러 대처법 4.그냥 for문과 비교 차례로 설명을 한다.
1. 사용 가능성 테스트 및 설정
병렬 처리에 앞서서 내가 쓰는 matlab에 병렬처리 toolbox가 있어서 되는지 여부를 먼저 봐야한다.
toolbox없으면 이 글을 볼 필요가 없다.
Command window에 다음과 같이 쳐본다.
license checkout distrib_Computing_toolbox
결과가 '1' 이 나오면 parallel computing이 가능하다는 것.
이제 설정을 하는 법을 보기로 한다. 아래의 사진들은 matlab 버전 2013a 가 기준이며 하위 버전의 경우에는 google에 검색하면 나온다.
아이콘 박스에서 parallel을 보면 'Manage Cluster Profiles'라고 있는데 그거 클릭.
찾아보니 matlab에서 최대 12개까지 지원 가능하다고 되어있고 default라고 되어있는데 그걸 12라고 바꿔준다.
왜 'Number of worker'를 12로 직접 입력했는가? 하면
처음에 그냥 default라고 되어있어서 그대로 두고 했더니, 아래와 같이 에러가 나서 직접 12로 수정해줬다.
validate.....어쩌구... 그래서 위에 있는 sub menu중에 validation results를 가서 아래와 같이 validate했는데,
이렇게 되었다. 그래서 'Number of workers'를 12로 바꾸어 준 것이다. 내가 틀려서 그런 이유가 아닐수 있으나 필자는 이런 방법으로 극복했다.
2. 사용 방법
1. 병렬처리를 적용할 시점의 맨 앞에 'matlabpool open 8; ' 을 써준다.
2. parfor i=1:N 즉, for 대신 parfor로 바꿔준다.
3. 끝내야 할 시점에 'matlabpool close; '
끝.
보기 쉽게 아래에 다시 쓰자면
%======================================================================
matlabpool open 8; % 필자는 쿼드코어 cpu 4개 이지만 스레드까지 해서 8개 가능하므로 8개를 열었다.
% 내용
parfor i=1:N
...............(내용)
end
matlabpool close;
%======================================================================
사용은 이렇게 하면 되는데 주의 해야 할 점이 있다.
병렬 처리는 동시에 input을 확 주워서 계산을 다같이 한번에 팍팍 한 뒤, output이 나오는 시스템이다.
따라서,
이런 경우에는 곤란하다.
1. 코어가 2개 이상이여서 병렬처리가 가능해야 하는 컴퓨터 (이건 뭐 당연함)
2. parfor 내부에 goto 같은 어디로 다시 가라는게 없어야됨
쉽게, 다같이 슉슉 계산해야 하는데 중간에 길이 틀어지면 안되니까.
3. parfor 내부에 종속되는 경우가 없어야 한다.
cnt = cnt +1; 과 같은 경우에는 현재의 값이 이전의 값의 영향을 받으므로 동시에 계산을 진행할 수 없다.
또한, 반복 할때마다 matrix의 크기가 달라진다 던지 하는 경우에도 해당 되는듯 하다.
4. parfor 내부에 새로운 선언이 없어야 한다.
동시에 for문이 진행되는데 선언이 있으면 동시에 같은게 여러개 선언 되는 것과 같은 이유로 안되는 듯.
결론 : 대충 요럴때 쓰면 좋다.
1. cell에 matrix를 넣는 경우
2. matrix에서 찾거나 뽑아 내고 싶은게 있어서 if 넣어서 연산할때.
3. 단순 계산
등등
3. 몇가지 에러 및 대처법
에러 유형 1
잘 되나 보려고 필자가 Command window에
쳐보니 잘된다.
그래서 바로 작성 중이던 코드에 matlabpool open 4; 를 위에 넣고 아래에 matlabpool close;를 넣었다.
그런데 아래와 같은 에러가 떳다.
이유는 Command window든, m-file이든 일단 한번 matlabpool을 open 시키면 close를 한 뒤에 다시 open을 시켜야 한다.
즉, matlabpool 을 open 시킨 상태중에서 다시 matlabpool open을 만나면 위와 같은 에러를 발생한다.
에러 유형 2
진짜 많이 고생했던 에러이다. "Error using matlabpool (line 144)"
google을 깨끗이 뒤졌으나 line 144에 대한 Caused by에 'The interactive communicating job failed with no massage"에 대한 해결은 없었다.
어떤 사람이 툴박스 설치때 문제일수 있으니 다시 matlab을 설치해 보라는 말이 있어서 재설치를 했지만 결과는 같았다.
mathworks에 올린 외국인들의 추축성 글을 따라하면 해결이 안된다.
필자의 데스크탑에는 두개의 백신 프로그램이 있는데 "알약" 그리고 "V3"이다.
알약의 경우에는 line 144번 에러와 무관하다.
문제는 V3이다. (안철수 형님이 너무 튼튼하게 만드심 ㅠㅠ)
V3가 가지고 있는 기능 중에 """ 개인 방화벽""" 을 꺼주면 바로 validate가 되어서 line 144 에러를 해결할수 있다.
4. For VS. Parfor
For 문과 Parfor는 얼마나 차이가 날까?
테스트를 위한 code를 찾던 중에 어떤 분의 블로그에 있는 코드를 가져와 테스트를 해보았다. 테스트 코드는 아래 참조 링크로 가시면 복사해서 사용하면 된다.
Ref : http://blog.naver.com/alsfelt?Redirect=Log&logNo=20078084220
여기에 올려진 테스트 코드를 이용하여 비교한 결과.
필자는 쿼드코어(4개)에 스레드까지 해서 8개로 돌렸다. 엄청난 차이를 볼수 있다.
그리고 windows의 작업관리자의 리소스모니터를 열어서 보면
병렬처리를 하지 않을 경우(matlabpool 을 안쓸때) 에는 1개만 돌아가는데
병렬 처리를 하면 8개가 돌아간다 (필자는 8개로 했으므로)
병렬처리는 적은 양의 경우에는 오히려 for문보다 성능이 안좋을 수도 있다고 하고 제약조건도 까다롭지만 매우 효율 적이므로 주로 많은 계산을 요구하면서 적용 가능한 for문의 경우에는 for문 대신에 parfor를 사용해야겠다.
<Parfor 작성시 유의점 (Sliced Variables>
ref : http://kr.mathworks.com/help/distcomp/sliced-variables.html
A sliced variable is one whose value can be broken up into segments, or slices, which are then operated on separately by different workers. Each iteration of the loop works on a different slice of the array. Using sliced variables is important because this type of variable can reduce communication between the client and workers. Only those slices needed by a worker are sent to it, and only when it starts working on a particular range of indices.
In the this example, a slice of A
consists of a single element of that array:
parfor i = 1:length(A) B(i) = f(A(i)); end
A variable in a parfor
-loop is sliced if it has all of the following characteristics. A description of each characteristic follows the list:
()
, or braces, {}
.[]
or ''
, because these operators attempt to delete elements.Type of First-Level Indexing. For a sliced variable, the first level of indexing is enclosed in either parentheses, ()
, or braces, {}
.
This table lists the forms for the first level of indexing for arrays sliced and not sliced.
Reference for Variable Not Sliced
A.x
A.(...)
Reference for Sliced Variable
A(...)
A{...}
After the first level, you can use any type of valid MATLAB® indexing in the second and further levels.
The variable A
shown here on the left is not sliced; that shown on the right is sliced:
Fixed Index Listing. Within the first-level parentheses or braces of a sliced variable's indexing, the list of indices is the same for all occurrences of a given variable.
The variable A
shown here on the left is not sliced because A
is indexed by i
and i+1
in different places; the code on the right shown on the right slices A
:
A.q{i,12} A{i,12}.q
Not sliced
Sliced
parfor i = 1:k B(:) = h(A(i), A(i+1)); end
parfor i = 1:k B(:) = f(A(i)); C(:) = g(A{i}); end
The example above on the right shows some occurrences of a sliced variable with first-level parenthesis indexing and with first-level brace indexing in the same loop. This is acceptable.
The following example on the left does not slice A
because the indexing of A
is not the same in all places. The example on the right slices A
and B
. The indexing of A
is not the same as the indexing of B
, but all indexing of A
is consistent, and all indexing of B
is consistent.
Not sliced
Sliced
parfor i=1:10 b = A(1,i) + A(2,i) end
A = [ 1 2 3 4 5 6 7 8 9 10; 10 20 30 40 50 60 70 80 90 100]; B = zeros(1,10); parfor i=1:10 for n=1:2 B(i) = B(i)+A(n,i) end end
Form of Indexing. Within the list of indices for a sliced variable, one of these indices is of the form i
, i+k
, i-k
, k+i
, ork-i
, where i
is the loop variable and k
is a constant or a simple (nonindexed) broadcast variable; and every other index is a scalar constant, a simple broadcast variable, a nested for
-loop index, colon, or end
.
With i
as the loop variable, the A
variables shown here on the left are not sliced; those on the right are sliced:
Not sliced
Sliced
A(i+f(k),j,:,3) % f(k) invalid for slicing A(i,20:30,end) % 20:30 not scalar A(i,:,s.field1) % s.field1 not simple broadcast var
A(i+k,j,:,3) A(i,:,end) A(i,:,k)
When you use other variables along with the loop variable to index an array, you cannot set these variables inside the loop. In effect, such variables are constant over the execution of the entire parfor
statement. You cannot combine the loop variable with itself to form an index expression.
Shape of Array. A sliced variable must maintain a constant shape. The variable A
shown here on either line is not sliced:
A(i,:) = []; A(end + 1) = i;
The reason A
is not sliced in either case is because changing the shape of a sliced array would violate assumptions governing communication between the client and workers.
All sliced variables have the characteristics of being input or output. A sliced variable can sometimes have both characteristics. MATLAB transmits sliced input variables from the client to the workers, and sliced output variables from workers back to the client. If a variable is both input and output, it is transmitted in both directions.
In this parfor
-loop, r
is a sliced input variable and b
is a sliced output variable:
a = 0; z = 0; r = rand(1,10); parfor ii = 1:10 a = ii; z = z + ii; b(ii) = r(ii); end
However, if it is clear that in every iteration, every reference to an array element is set before it is used, the variable is not a sliced input variable. In this example, all the elements of A
are set, and then only those fixed values are used:
parfor ii = 1:n if someCondition A(ii) = 32; else A(ii) = 17; end loop code that uses A(ii) end
Even if a sliced variable is not explicitly referenced as an input, implicit usage might make it so. In the following example, not all elements of A
are necessarily set inside the parfor
-loop, so the original values of the array are received, held, and then returned from the loop, making A
both a sliced input and output variable.
A = 1:10; parfor ii = 1:10 if rand < 0.5 A(ii) = 0; end end