다음에 대해 다음과 같은 간단한 속도 테스트를 고려하십시오 arrayfun
.
T = 4000;
N = 500;
x = randn(T, N);
Func1 = @(a) (3*a^2 + 2*a - 1);
tic
Soln1 = ones(T, N);
for t = 1:T
for n = 1:N
Soln1(t, n) = Func1(x(t, n));
end
end
toc
tic
Soln2 = arrayfun(Func1, x);
toc
내 컴퓨터 (Linux Mint 12의 Matlab 2011b)에서이 테스트의 출력은 다음과 같습니다.
Elapsed time is 1.020689 seconds.
Elapsed time is 9.248388 seconds.
뭐야?!? arrayfun
, 분명히 깨끗해 보이는 솔루션이지만 훨씬 느립니다. 여기서 무슨 일이 일어나고 있습니까?
또한 비슷한 스타일의 테스트를 수행 한 cellfun
결과 명시 적 루프보다 약 3 배 느린 것으로 나타났습니다. 다시 말하지만이 결과는 내가 예상했던 것과 반대입니다.
내 질문은 : 왜 arrayfun
그리고 cellfun
너무 느린? 그리고이 점을 감안할 때 코드를보기 좋게 만드는 것 외에 사용하는 좋은 이유가 있습니까?
참고 :arrayfun
여기서는 병렬 처리 도구 상자의 GPU 버전이 아닌 표준 버전에 대해 이야기하고 있습니다 .
편집 : 분명히하기 위해 Func1
Oli가 지적한대로 위의 벡터화가 가능 하다는 것을 알고 있습니다. 실제 질문을 위해 간단한 속도 테스트를 제공하기 때문에 선택했습니다.
편집 : grungetta의 제안에 따라 feature accel off
. 결과는 다음과 같습니다.
Elapsed time is 28.183422 seconds.
Elapsed time is 23.525251 seconds.
즉, 차이점의 큰 부분은 JIT 가속기가 명시 적 for
루프 속도를 높이는 것보다 훨씬 더 나은 작업을 수행한다는 것 arrayfun
입니다. 이것은 arrayfun
실제로 더 많은 정보를 제공하기 때문에 나에게는 이상해 보인다 . 즉, 그 사용은 호출 순서가 Func1
중요하지 않다는 것을 보여주기 때문이다. 또한 JIT 가속기가 켜져 있든 꺼져 있든 내 시스템은 하나의 CPU 만 사용합니다.
답변
다른 버전의 코드를 실행하여 아이디어를 얻을 수 있습니다. 루프에서 함수를 사용하는 대신 명시 적으로 계산을 작성하는 것이 좋습니다.
tic
Soln3 = ones(T, N);
for t = 1:T
for n = 1:N
Soln3(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
end
end
toc
내 컴퓨터에서 계산하는 시간 :
Soln1 1.158446 seconds.
Soln2 10.392475 seconds.
Soln3 0.239023 seconds.
Oli 0.010672 seconds.
이제 완전히 ‘벡터화 된’솔루션이 분명히 가장 빠르지 만 모든 x 항목에 대해 호출 할 함수를 정의하는 것은 엄청난 오버 헤드 임을 알 수 있습니다 . 계산을 명시 적으로 작성하는 것만으로도 요소 5의 속도가 빨라졌습니다. 이것은 MATLABs JIT 컴파일러 가 인라인 함수를 지원하지 않는다는 것을 보여줍니다 . 거기 gnovice의 대답에 따르면 실제로 익명의 함수보다 정상적인 함수를 작성하는 것이 좋습니다. 시도 해봐.
다음 단계-내부 루프 제거 (벡터화) :
tic
Soln4 = ones(T, N);
for t = 1:T
Soln4(t, :) = 3*x(t, :).^2 + 2*x(t, :) - 1;
end
toc
Soln4 0.053926 seconds.
또 다른 요소 5 속도 향상 :이 문장에 MATLAB에서 루프를 피해야한다는 내용이 있습니다 … 아니면 정말 있습니까? 그럼 이것 좀 봐
tic
Soln5 = ones(T, N);
for n = 1:N
Soln5(:, n) = 3*x(:, n).^2 + 2*x(:, n) - 1;
end
toc
Soln5 0.013875 seconds.
‘완전한’벡터화 된 버전에 훨씬 더 가깝습니다. Matlab은 행렬을 열 단위로 저장합니다. 가능한 경우 항상 계산을 ‘열 단위’로 벡터화하도록 구조화해야합니다.
이제 Soln3로 돌아갈 수 있습니다. 루프 순서는 ‘행 방향’입니다. 그것을 바꿀 수 있습니다
tic
Soln6 = ones(T, N);
for n = 1:N
for t = 1:T
Soln6(t, n) = 3*x(t, n)^2 + 2*x(t, n) - 1;
end
end
toc
Soln6 0.201661 seconds.
더 좋지만 여전히 매우 나쁩니다. 단일 루프-좋습니다. 이중 루프-나쁨. MATLAB이 루프의 성능을 개선하기 위해 적절한 작업을 수행했다고 생각하지만 여전히 루프 오버 헤드가 있습니다. 내부에 더 무거운 작업이 있다면 눈치 채지 못할 것입니다. 그러나이 계산은 메모리 대역폭 제한이 있으므로 루프 오버 헤드를 볼 수 있습니다. 그리고 당신은 것 보다 더 명확하게이 FUNC1를 호출의 오버 헤드를 참조하십시오.
그래서 arrayfun은 무엇입니까? 거기에도 기능이 포함되어 있지 않으므로 많은 오버 헤드가 발생합니다. 그러나 이중 중첩 루프보다 훨씬 더 나쁜 이유는 무엇입니까? 실제로 cellfun / arrayfun 사용에 대한 주제는 여러 번 광범위하게 논의되었습니다 (예 : here , here , here 및 here ). 이러한 함수는 단순히 느리기 때문에 이러한 세분화 계산에 사용할 수 없습니다. 코드 간결함과 셀과 배열 간의 멋진 변환을 위해 사용할 수 있습니다. 그러나 함수는 작성한 것보다 무거워 야합니다.
tic
Soln7 = arrayfun(@(a)(3*x(:,a).^2 + 2*x(:,a) - 1), 1:N, 'UniformOutput', false);
toc
Soln7 0.016786 seconds.
Soln7은 이제 셀입니다. 때로는 유용합니다. 이제 코드 성능이 상당히 좋으며 출력으로 셀이 필요한 경우 완전히 벡터화 된 솔루션을 사용한 후 행렬을 변환 할 필요가 없습니다.
그렇다면 arrayfun이 단순한 루프 구조보다 느린 이유는 무엇입니까? 안타깝게도 사용 가능한 소스 코드가 없기 때문에 확실히 말할 수 없습니다. arrayfun은 모든 종류의 다양한 데이터 구조와 인수를 처리하는 범용 함수이기 때문에 루프 중첩으로 직접 표현할 수있는 간단한 경우에는 반드시 매우 빠르지는 않습니다. 오버 헤드가 어디서 오는지 알 수 없습니다. 더 나은 구현으로 오버 헤드를 피할 수 있습니까? 아마. 그러나 불행히도 우리가 할 수있는 유일한 일은 성능을 연구하여 잘 작동하는 경우와 그렇지 않은 경우를 식별하는 것입니다.
업데이트이 테스트의 실행 시간이 짧기 때문에 신뢰할 수있는 결과를 얻기 위해 테스트 주변에 루프를 추가했습니다.
for i=1:1000
% compute
end
아래에 주어진 시간 :
Soln5 8.192912 seconds.
Soln7 13.419675 seconds.
Oli 8.089113 seconds.
arrayfun은 여전히 나쁘지만 벡터화 된 솔루션보다 적어도 3 배 더 나쁘지는 않습니다. 반면에 열 단위 계산을 사용하는 단일 루프는 완전히 벡터화 된 버전만큼 빠릅니다.이 모든 작업은 단일 CPU에서 수행되었습니다. Soln5 및 Soln7의 결과는 2 코어로 전환해도 변경되지 않습니다. Soln5에서는 parfor를 사용하여 병렬화해야합니다. 속도 향상은 잊어 버리세요 … Soln7은 arrayfun이 병렬로 실행되지 않기 때문에 병렬로 실행되지 않습니다. 반면 Olis 벡터화 버전 :
Oli 5.508085 seconds.
답변
왜냐하면 !!!!
x = randn(T, N);
gpuarray
유형 이 아닙니다 .
당신이해야 할 일은
x = randn(T, N,'gpuArray');