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

내가 실험하고있어 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 구현에 대한 정의를 찾는 데 시간이 걸리는 반면이 오버 헤드는 직접 경로에있는 함수에는 없는가?



답변

나는 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


답변

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

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


답변

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


답변

실제로 코드에는 문제가 없지만 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를 사용하지 말고 대신 구조를 그룹화하는 것이 좋습니다.


답변