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.