MATLAB OOP가 느리거나 잘못하고 있습니까?


144

내가 실험하고있어 MATLAB OOP A가 내 C ++의 로거 클래스를 모방 시작으로, 나는이 같은 일을 할 수있는 것이 좋은 것 생각하고, 문자열 클래스에 내 모든 문자열 도우미 기능을 가하고있어 a + b, a == b, a.find( b )대신 strcat( a b ), strcmp( a, b ), strfind( a, b )등의 첫 번째 요소 검색

문제 : 둔화

나는 위의 것들을 사용하고 즉시 급격한 둔화를 발견했습니다 . 내가 잘못하고 있습니까 (MATLAB 경험이 다소 제한되어 있기 때문에 가능할 수도 있음) MATLAB의 OOP가 많은 오버 헤드를 유발합니까?

내 테스트 사례

다음은 문자열에 대한 간단한 테스트입니다. 기본적으로 문자열을 추가하고 추가 된 부분을 다시 제거하면됩니다.

참고 : 실제로 실제 코드에서 이와 같은 String 클래스를 작성하지 마십시오! Matlab에는 string현재 기본 배열 유형이 있으므로 대신 사용해야합니다.

classdef String < handle
  ....
  properties
    stringobj = '';
  end
  function o = plus( o, b )
    o.stringobj = [ o.stringobj b ];
  end
  function n = Length( o )
    n = length( o.stringobj );
  end
  function o = SetLength( o, n )
    o.stringobj = o.stringobj( 1 : n );
  end
end

function atest( a, b ) %plain functions
  n = length( a );
  a = [ a b ];
  a = a( 1 : n );

function btest( a, b ) %OOP
  n = a.Length();
  a = a + b;
  a.SetLength( n );

function RunProfilerLoop( nLoop, fun, varargin )
  profile on;
  for i = 1 : nLoop
    fun( varargin{ : } );
  end
  profile off;
  profile report;

a = 'test';
aString = String( 'test' );
RunProfilerLoop( 1000, @(x,y)atest(x,y), a, 'appendme' );
RunProfilerLoop( 1000, @(x,y)btest(x,y), aString, 'appendme' );

결과

1000 회 반복에 대한 총 시간 (초) :

btest 0.550 (String.SetLength 0.138, String.plus 0.065, String.Length 0.057 사용)

증명 0.015

로거 시스템의 결과는 마찬가지로 frpintf( 1, 'test\n' )String 클래스를 내부적으로 사용할 때 1000 호출에 대해 0.1 초 , 내 시스템에 1000 호출에 대해 7 (!) 초 (OK, 더 많은 논리가 있지만 C ++과 비교) : 사용 내 시스템의 오버 헤드 std::string( "blah" )std::cout일반 대 출력측은 std::cout << "blah"1 밀리 초 정도이다.)

클래스 / 패키지 함수를 찾을 때 오버 헤드입니까?

MATLAB은 해석되므로 런타임시 함수 / 객체의 정의를 찾아야합니다. 그래서 경로에있는 함수 대 클래스 또는 패키지 함수를 찾는 데 훨씬 더 많은 오버 헤드가 관련되어 있는지 궁금합니다. 나는 이것을 테스트하려고 시도했고, 그것은 단지 낯선 사람이됩니다. 클래스 / 객체의 영향을 배제하기 위해 경로의 함수와 패키지의 함수를 호출하는 것을 비교했습니다.

function n = atest( x, y )
  n = ctest( x, y ); % ctest is in matlab path

function n = btest( x, y )
  n = util.ctest( x, y ); % ctest is in +util directory, parent directory is in path

결과는 위와 같은 방식으로 수집되었습니다.

atest 0.004 sec, ctest에서 0.001 초

util.ctest에서 btest 0.060 초, 0.014 초

MATLAB에서 OOP 구현에 대한 정의를 찾는 데 시간이 걸리는 반면이 오버 헤드는 직접 경로에있는 함수에는 없는가?


5
이 질문에 감사드립니다! Matlab 힙 (OOP / 클로저)의 성능이 몇 년 동안 문제를 일으켰습니다 . stackoverflow.com/questions/1446281/matlabs-garbage-collector를 참조하십시오 . MatlabDoug / Loren / MikeKatz가 귀하의 게시물에 어떤 반응을 보일지 궁금합니다.
Mikhail

1
^ 그것은 흥미있는 읽기이었다.
stijn

1
@ MatlabDoug : 동료 Mike Karr이 OP를 언급 할 수 있습니까?
Mikhail

4
독자는 최신 R2012a 버전의 OOP 성능에 관한 최근 블로그 게시물 (데이브 포티 (Dave Foti))을 확인해야합니다. 객체 지향 MATLAB 코드의 성능 고려
Amro

1
하위 요소의 메소드 호출이 루프에서 제거되는 코드 구조의 민감도에 대한 간단한 예입니다. for i = 1:this.get_n_quantities() if(strcmp(id,this.get_quantity_rlz(i).get_id())) ix = i; end end2.2 초, nq = this.get_n_quantities(); a = this.get_quantity_realizations(); for i = 1:nq c = a{i}; if(strcmp(id,c.get_id())) ix = i; end end0.01, 2 배의 매기 주문
Jose Ospina 2016 년

답변:


223

나는 OO MATLAB과 한동안 일하고 있었고 비슷한 성능 문제를 보았습니다.

짧은 대답은 그렇습니다. MATLAB의 OOP는 느립니다. 주류 OO 언어보다 높은 메소드 호출 오버 헤드가 있으며 이에 대해 할 수있는 일은 많지 않습니다. 그 이유 중 하나는 관용적 MATLAB이 "벡터화 된"코드를 사용하여 메소드 호출 수를 줄이고 호출 당 오버 헤드가 우선 순위가 높지 않기 때문일 수 있습니다.

나는 아무 것도없는 "nop"함수를 다양한 유형의 함수와 메소드로 작성하여 성능을 벤치마킹했습니다. 다음은 일반적인 결과입니다.

>> call_nops
컴퓨터 : PCWIN 출시 : 2009b
각 함수 / 메소드를 100,000 번 호출
nop () 함수 : 호출 당 0.02261 초 0.23 usec
nop1-5 () 함수 : 호출 당 0.02182 초 0.22 usec
nop () 하위 함수 : 호출 당 0.02244 초 0.22 usec
@ () [] 익명 함수 : 호출 당 0.08461 초 0.85 usec
nop (obj) 방법 : 0.24664 초 2.47 회 호출 당
nop1-5 (obj) 메서드 : 호출 당 0.23469 초 2.35 usec
nop () 개인 함수 : 호출 당 0.02197 초 0.22 usec
classdef nop (obj) : 통화 당 0.90547 초 9.05 usec
classdef obj.nop () : 1.75522 초 17.55 호출 당 usec
classdef private_nop (obj) : 호출 당 0.84738 초 8.47 usec
classdef nop (obj) (m-file) : 0.90560 초 9.06 통화 당 usec
classdef class.staticnop () : 1.16361 초 11.64 통화 당
Java nop () : 통화 당 2.43035 초 24.30 usec
Java static_nop () : 호출 당 0.87682 초 8.77 usec
Java의 Java nop () : 호출 당 0.00014 초 0.00 usec
MEX mexnop () : 호출 당 0.11409 초 1.14 usec
C nop () : 통화 당 0.00001 초 0.00 usec

R2008a에서 R2009b까지 비슷한 결과. 32 비트 MATLAB을 실행하는 Windows XP x64에 있습니다.

"Java nop ()"는 M 코드 루프 내에서 호출되는 Java가 아닌 메소드이며 각 호출마다 MATLAB-to-Java 디스패치 오버 헤드를 포함합니다. "Java에서 Java nop ()"는 Java for () 루프에서 호출되는 것과 동일하며 해당 경계 불이익을 일으키지 않습니다. 소금 한 알로 Java 및 C 타이밍을 가져 가십시오. 영리한 컴파일러는 호출을 완전히 최적화 할 수 있습니다.

패키지 범위 지정 메커니즘은 새롭고 classdef 클래스와 거의 동시에 도입되었습니다. 동작이 관련 될 수 있습니다.

몇 가지 임시 결론 :

  • 방법은 함수보다 느립니다.
  • 새로운 스타일 (classdef) 메소드는 이전 스타일 메소드보다 느립니다.
  • 새로운 obj.nop()구문은 nop(obj)classdef 객체의 동일한 메소드에서도 구문 보다 느립니다 . Java 객체 (표시되지 않음)와 동일합니다. 당신이 빨리 가고 싶은 경우, 전화 nop(obj).
  • Windows의 64 비트 MATLAB에서 메서드 호출 오버 헤드가 더 높습니다 (약 2 배). (표시되지 않음)
  • MATLAB 메소드 디스패치가 다른 언어보다 느립니다.

이것이 왜 그렇게 말하는지는 내 부분에 대한 추측 일뿐입니다. MATLAB 엔진의 OO 내부는 공개되지 않습니다. MATLAB에는 JIT가 있지만 해석 및 컴파일 된 문제 자체는 아니지만 MATLAB의 느슨한 타이핑 및 구문은 런타임에 더 많은 작업을 의미 할 수 있습니다. (예를 들어 "f (x)"가 함수 호출인지 또는 배열에 대한 인덱스인지 구문만으로는 알 수 없습니다. 런타임시 작업 공간의 상태에 따라 다릅니다.) MATLAB의 클래스 정의가 연결되어 있기 때문일 수 있습니다 다른 많은 언어들은 그렇지 않은 방식으로 파일 시스템 상태로

그래서 뭘 할건데?

이에 대한 관용적 인 MATLAB 접근 방식은 객체 인스턴스가 배열을 래핑하도록 클래스 정의를 구성하여 코드를 "벡터화"하는 것입니다. 즉, 각 필드는 병렬 배열을 보유합니다 (MATLAB 설명서에서 "평면"구성이라고 함). 각각 스칼라 값을 보유하는 필드가있는 객체의 배열을 갖는 대신, 스스로 배열 인 객체를 정의하고, 메소드가 배열을 입력으로 취하고 필드와 입력에 대해 벡터화 된 호출을 수행하게합니다. 이렇게하면 디스패치 오버 헤드가 병목 현상이되지 않도록 메소드 호출 수가 줄어 듭니다.

MATLAB에서 C ++ 또는 Java 클래스를 흉내내는 것은 아마도 최적이 아닐 것입니다. Java / C ++ 클래스는 일반적으로 객체가 가능한 한 가장 작은 빌딩 블록 (즉, 다양한 클래스)으로 작성되고 배열, 컬렉션 객체 등으로 작성하고 루프를 통해 반복합니다. 빠른 MATLAB 클래스를 만들려면 해당 접근 방식을 안쪽으로 돌리십시오. 필드가 배열 인 더 큰 클래스가 있고 해당 배열에서 벡터화 된 메소드를 호출하십시오.

요점은 배열 핸들링, 벡터화 된 수학과 같은 언어의 장점에 따라 코드를 정렬하고 약점을 피하는 것입니다.

편집 : 원래 게시물 이후 R2010b 및 R2011a가 나왔습니다. MCOS 호출이 약간 빨라지고 Java 및 이전 스타일 메소드 호출이 느려져 전체적인 그림은 동일 합니다.

편집 : 여기에 함수 호출 타이밍의 추가 테이블과 함께 "경로 감도"에 대한 메모가 있었는데, 여기서 함수 시간은 Matlab 경로 구성 방법에 의해 영향을 받았지만, 특정 네트워크 설정의 수차 인 것처럼 보입니다. 시간. 위의 차트는 시간이 지남에 따라 테스트의 우세한 시간을 반영합니다.

업데이트 : R2011b

편집 (2012 년 2 월 13 일) : R2011b가 종료되었으며 성능 사진이이를 업데이트하기에 충분하도록 변경되었습니다.

아치 : PCWIN 출시 : 2011b 
머신 : R2011b, Windows XP, 8x Core i7-2600 @ 3.40GHz, 3GB RAM, NVIDIA NVS 300
각 작업을 100,000 번 수행
통화 당 총 스타일 µsec
nop () 함수 : 0.01578 0.16
nop (), 10 배 루프 언롤 : 0.01477 0.15
nop (), 100x 루프 언롤 : 0.01518 0.15
nop () 하위 함수 : 0.01559 0.16
@ () [] 익명 함수 : 0.06400 0.64
nop (obj) 방법 : 0.28482 2.85
nop () 전용 함수 : 0.01505 0.15
classdef nop (obj) : 0.43323 4.33
classdef obj.nop () : 0.81087 8.11
classdef private_nop (obj) : 0.32272 3.23
classdef class.staticnop () : 0.88959 8.90
classdef 상수 : 1.51890 15.19
classdef 속성 : 0.12992 1.30
게터가있는 classdef 속성 : 1.39912 13.99
+ pkg.nop () 함수 : 0.87345 8.73
+ pkg 내부에서 + pkg.nop () : 0.80501 8.05
자바 obj.nop () : 1.86378 18.64
자바 nop (obj) : 0.22645 2.26
자바 feval ( 'nop', obj) : 0.52544 5.25
자바 Klass.static_nop () : 0.35357 3.54
Java의 Java obj.nop () : 0.00010 0.00
MEX mexnop () : 0.08709 0.87
C nop () : 0.00001 0.00
j () (내장) : 0.00251 0.03

나는 이것의 결론은 다음과 같다고 생각한다.

  • MCOS / classdef 메소드가 더 빠릅니다. foo(obj)구문 을 사용하는 한 비용은 이제 이전 스타일 클래스와 비슷 합니다. 따라서 메소드 속도는 더 이상 대부분의 경우 이전 스타일 클래스를 고수하는 이유가 아닙니다. (Kudos, MathWorks!)
  • 네임 스페이스에 함수를 넣으면 속도가 느려집니다. (R2011b에서는 새로운 것이 아니라 내 테스트에서는 새로운 것입니다.)

업데이트 : R2014a

벤치마킹 코드를 재구성하고 R2014a에서 실행했습니다.

PCWIN64의 Matlab R2014a  
PCWIN64 Windows 7 6.1의 Matlab 8.3.0.532 (R2014a) / Java 1.7.0_11 (eilonwy-win7) 
머신 : Core i7-3615QM CPU @ 2.30GHz, 4GB RAM (VMware 가상 플랫폼)
니터 = 100000 

작동 시간 (µsec)  
nop () 함수 : 0.14 
nop () 하위 함수 : 0.14 
@ () [] 익명 함수 : 0.69 
nop (obj) 방법 : 3.28 
@class의 nop () private fcn : 0.14 
classdef nop (obj) : 5.30 
classdef obj.nop () : 10.78 
classdef pivate_nop (obj) : 4.88 
classdef class.static_nop () : 11.81 
classdef 상수 : 4.18 
classdef 속성 : 1.18 
게터가있는 classdef 속성 : 19.26 
+ pkg.nop () 함수 : 4.03 
+ pkg 내부에서 + pkg.nop () : 4.16 
feval ( 'nop') : 2.31 
feval (@nop) : 0.22 
eval ( 'nop') : 59.46 
자바 obj.nop () : 26.07 
자바 nop (obj) : 3.72 
자바 feval ( 'nop', obj) : 9.25 
자바 Klass.staticNop () : 10.54 
Java의 Java obj.nop () : 0.01 
MEX mexnop () : 0.91 
내장 j () : 0.02 
s.foo 필드 접근 : 0.14 
비어 있음 (지속적) : 0.00 

업데이트 : R2015b : 객체가 더 빨라졌습니다!

@Shaked가 친절하게 제공 한 R2015b 결과는 다음과 같습니다. 이것은 변화입니다. OOP가 훨씬 빠르며 obj.method()구문은 method(obj)기존 OOP 객체보다 빠르며 훨씬 빠릅니다.

PCWIN64의 Matlab R2015b  
PCWIN64 Windows 8 6.2의 Matlab 8.6.0.267246 (R2015b) / Java 1.7.0_60 (손떨림) 
머신 : Core i7-4720HQ CPU @ 2.60GHz, 16GB RAM (20378)
니터 = 100000 

작동 시간 (µsec)  
nop () 함수 : 0.04 
nop () 하위 함수 : 0.08 
@ () [] 익명 함수 : 1.83 
nop (obj) 방법 : 3.15 
@class의 nop () private fcn : 0.04 
classdef nop (obj) : 0.28 
classdef obj.nop () : 0.31 
classdef pivate_nop (obj) : 0.34 
classdef class.static_nop () : 0.05 
classdef 상수 : 0.25 
classdef 속성 : 0.25 
게터가있는 classdef 속성 : 0.64 
+ pkg.nop () 함수 : 0.04 
+ pkg 내부에서 + pkg.nop () : 0.04 
feval ( 'nop') : 8.26 
feval (@nop) : 0.63 
평가 ( 'nop') : 21.22 
자바 obj.nop () : 14.15 
자바 nop (obj) : 2.50 
자바 feval ( 'nop', obj) : 10.30 
자바 Klass.staticNop () : 24.48 
Java의 Java obj.nop () : 0.01 
MEX mexnop () : 0.33 
내장 j () : 0.15 
s.foo 필드 접근 : 0.25 
비어 있음 (영구적) : 0.13 

업데이트 : R2018a

다음은 R2018a 결과입니다. 새로운 실행 엔진이 R2015b에 도입되었을 때 보았던 큰 도약은 아니지만 여전히 매년 개선되고 있습니다. 특히 익명 함수 핸들이 더 빨라졌습니다.

MACI64의 Matlab R2018a  
MACI64 Mac OS X 10.13.5의 Matlab 9.4.0.813654 (R2018a) / Java 1.8.0_144 (eilonwy) 
머신 : Core i7-3615QM CPU @ 2.30GHz, 16GB RAM 
니터 = 100000 

작동 시간 (µsec)  
nop () 함수 : 0.03 
nop () 하위 함수 : 0.04 
@ () [] 익명 함수 : 0.16 
classdef nop (obj) : 0.16 
classdef obj.nop () : 0.17 
classdef pivate_nop (obj) : 0.16 
classdef class.static_nop () : 0.03 
classdef 상수 : 0.16 
classdef 속성 : 0.13 
게터가있는 classdef 속성 : 0.39 
+ pkg.nop () 함수 : 0.02 
+ pkg 내부에서 + pkg.nop () : 0.02 
feval ( 'nop') : 15.62 
feval (@nop) : 0.43 
eval ( 'nop') : 32.08 
자바 obj.nop () : 28.77 
자바 nop (obj) : 8.02 
자바 feval ( 'nop', obj) : 21.85 
자바 Klass.staticNop () : 45.49 
Java의 Java obj.nop () : 0.03 
MEX mexnop () : 3.54 
내장 j () : 0.10 
s.foo 필드 접근 : 0.16 
비어 있음 (영구적) : 0.07 

업데이트 : R2018b 및 R2019a : 변경 사항 없음

큰 변화가 없습니다. 나는 테스트 결과를 포함시키는 것을 귀찮게하지 않습니다.

벤치 마크 소스 코드

이 벤치 마크에 대한 소스 코드를 MIT 라이센스에 따라 릴리스 된 GitHub에 올렸습니다. https://github.com/apjanke/matlab-bench


5
@AndrewJanke R2012a로 벤치 마크를 다시 실행할 수 있다고 생각하십니까? 정말 흥미 롭습니다.
Dang Khoa

7
안녕 여러분. 여전히 소스 코드에 관심이 있다면, 그것을 재구성하고 GitHub에서 공개 소스로 만들었습니다. github.com/apjanke/matlab-bench
Andrew Janke

2
@Seeda : 정적 결과는이 결과에서 "classdef class.static_nop ()"로 나열됩니다. 그것들은 기능에 비해 상당히 느립니다. 자주 전화하지 않으면 문제가되지 않습니다.
Andrew Janke

2
여기 @AndrewJanke 그것입니다 gist.github.com/ShakedDovrat/62db9e8f6883c5e28fc0
진탕

2
와! 이러한 결과가 유지되면이 전체 답변을 수정해야 할 수도 있습니다. 추가되었습니다. 감사!
Andrew Janke

3

핸들 클래스에는 정리 목적으로 모든 참조를 추적하는 데 따른 추가 오버 헤드가 있습니다.

핸들 클래스를 사용하지 않고 동일한 실험을 시도하고 결과를 확인하십시오.


1
String과 동일한 실험이지만 이제는 다른 컴퓨터에서 값 클래스로 사용됩니다. atest : 0.009, btest : o.356. 그것은 기본적으로 핸들과 동일한 차이점이므로 추적 참조가 핵심 답변이라고 생각하지 않습니다. 또한 패키지의 함수와 함수의 오버 헤드에 대해서는 설명하지 않습니다.
stijn 2009

어떤 MATLAB 버전을 사용하고 있습니까?
MikeEL

1
핸들과 값 클래스 사이에서 비슷한 비교를 수행했으며 둘 사이의 성능 차이를 보지 못했습니다.
RjOllos

나는 더 이상 차이도 느끼지 못한다.
MikeEL

이해하기 : Matlab에서 쓰기 객체에 복사 및 공유 된 기본 원시 데이터를 사용하기 때문에 객체를 처리하는 것이 아니라 모든 배열이 참조 횟수를 계산합니다.
Andrew Janke

1

OO 성능은 사용 된 MATLAB 버전에 따라 크게 다릅니다. 모든 버전에 대해 언급 할 수는 없지만 2012a가 2010 버전보다 훨씬 개선 된 경험을 알고 있습니다. 벤치 마크가 없으므로 제시 할 숫자가 없습니다. 핸들 클래스를 사용하여 독점적으로 작성되고 2012a로 작성된 내 코드는 이전 버전에서는 전혀 실행되지 않습니다.


1

실제로 코드에는 문제가 없지만 Matlab에는 문제가 있습니다. 나는 그것이 일종의 장난처럼 보인다고 생각합니다. 클래스 코드를 컴파일하는 것은 오버 헤드에 지나지 않습니다. 간단한 클래스 포인트 (한 번 핸들)와 다른 (한 번 값 클래스)로 테스트를 수행했습니다.

    classdef Pointh < handle
    properties
       X
       Y
    end  
    methods        
        function p = Pointh (x,y)
            p.X = x;
            p.Y = y;
        end        
        function  d = dist(p,p1)
            d = (p.X - p1.X)^2 + (p.Y - p1.Y)^2 ;
        end

    end
end

여기 시험이있다

%handle points 
ph = Pointh(1,2);
ph1 = Pointh(2,3);

%values  points 
p = Pointh(1,2);
p1 = Pointh(2,3);

% vector points
pa1 = [1 2 ];
pa2 = [2 3 ];

%Structur points 
Ps.X = 1;
Ps.Y = 2;
ps1.X = 2;
ps1.Y = 3;

N = 1000000;

tic
for i =1:N
    ph.dist(ph1);
end
t1 = toc

tic
for i =1:N
    p.dist(p1);
end
t2 = toc

tic
for i =1:N
    norm(pa1-pa2)^2;
end
t3 = toc

tic
for i =1:N
    (Ps.X-ps1.X)^2+(Ps.Y-ps1.Y)^2;
end
t4 = toc

결과 t1 =

12.0212 % 핸들

t2 =

12.0042 % 가치

t3 =

0.5489  % vector

t4 =

0.0707 % structure 

따라서 효율적인 성능을 위해서는 OOP를 사용하지 말고 대신 구조를 그룹화하는 것이 좋습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.