14

mex一般に、特定の計算を高速化するために、Matlab でファイル (c/c++ で記述)を呼び出すのは非常に簡単です。しかし、私の経験では、Matlab の真のボトルネックはデータ プロットです。ハンドルの作成には非常にコストがかかり、ハンドル データ (XData、YData、ZData など) のみを更新する場合でも、かなりの時間がかかる場合があります。さらに悪いことに、Matlab はシングル スレッド プログラムであるため、複数のプロットを同時に更新することはできません。

したがって、私の質問: Matlab GUI を作成し、プロット/視覚化を処理する C++ (またはその他の並列化可能なコード) を呼び出すことは可能ですか? Windows、Mac、Linux で動作するクロスプラットフォーム ソリューションを探していますが、いずれかの OS で開始できるソリューションは大歓迎です!

Matlab の構文を使用しているように見えるC++ ライブラリを見つけましたが、Matlab のウィンドウにプロットすると再び速度が低下するplot()のではないかと心配しているため、これで速度が向上するかどうかはわかりません。figure()

以前にこの種の状況に対処したことのある人からのコメントやフィードバックをいただければ幸いです。

編集:明らかに、私はすでに自分のコードをプロファイリングしており、ボトルネックはプロットです(大量のデータを持つ多数のパネル)。

EDIT2:あなたが報奨金を得るには、これを行う方法に関する実際の最小限の実用的な例が必要です-示唆に富む回答は役に立ちません。

EDIT3:プロットするデータについて: 最も単純なケースでは、1000000 データ ポイントのようなもので毎秒更新する必要がある 20 のライン プロットについて考えます。

EDIT4:これはプロットするポイントが膨大であることは知っていますが、問題が簡単だとは決して言いませんでした。実際にプロットする前に、どのポイントが重要かを評価する方法がないため、特定のデータ ポイントを除外することはできません (データはサブミリ秒の時間分解能でサンプリングされます)。実際のところ、私のデータは、データ ビューアー (c++ で記述) が付属している市販のデータ取得システムを使用して取得されています。このプログラムは、1000000 を超えるデータ ポイントを含む最大 60 の折れ線グラフを問題なく視覚化できます。

EDIT5:現在の議論の行き先が気に入らない. データをサブサンプリングすると速度が向上する可能性があることは承知していますが、これは問題ではありません。ここでの質問は、ac / c++ / python / Java インターフェイスを matlab で動作させる方法であり、ハードウェアに直接話しかけて (または他のトリック/方法を使用して) プロットを高速化することを願っています。

4

9 に答える 9

7

レンダリングメソッドをに変更するという簡単な解決策を試しましたOpenGLか?

opengl hardware;
set(gcf,'Renderer','OpenGL');

警告!このモードでは消えてしまうものがいくつかあり、少し異なって見えますが、特にハードウェアアクセラレータを使用している場合は、通常、プロットの実行速度が大幅に向上します。

ちなみに、実際にパフォーマンスが向上するのは確かですか?たとえば、私の経験では、WPFグラフィックスはC#Matlabsよりもかなり遅く、特に散布図と円です。

編集:実際に画面に描画されるポイントの数はそれほど多くないという事実について考えました。基本的には、画面にピクセルがある場所で補間する必要があることを意味します。このオブジェクトをチェックしてください:

classdef InterpolatedPlot < handle
    properties(Access=private)
        hPlot;
    end

    methods(Access=public)
        function this = InterpolatedPlot(x,y,varargin)
            this.hPlot = plot(0,0,varargin{:});
            this.setXY(x,y);
        end        
    end    

    methods
        function setXY(this,x,y)
            parent = get(this.hPlot,'Parent');
            set(parent,'Units','Pixels')
            sz = get(parent,'Position');
            width = sz(3); %Actual width in pixels
            subSampleX = linspace(min(x(:)),max(x(:)),width);

            subSampleY = interp1(x,y,subSampleX);
            set(this.hPlot,'XData',subSampleX,'YData',subSampleY);
        end

    end
end

そして、これがそれを使用する方法の例です:

function TestALotOfPoints()
    x = rand(10000,1);
    y = rand(10000,1);

    ip = InterpolatedPlot(x,y,'color','r','LineWidth',2);

end

別の可能な改善: また、xデータが並べ替えられている場合は、interp1qの代わりに使用できますinterp。これにより、はるかに高速になります。

classdef InterpolatedPlot < handle
    properties(Access=private)
        hPlot;
    end

%     properties(Access=public)
%         XData;
%         YData;      
%     end

    methods(Access=public)
        function this = InterpolatedPlot(x,y,varargin)
            this.hPlot = plot(0,0,varargin{:});
            this.setXY(x,y);
%             this.XData = x;
%             this.YData = y;
        end        
    end    

    methods
        function setXY(this,x,y)
            parent = get(this.hPlot,'Parent');
            set(parent,'Units','Pixels')
            sz = get(parent,'Position');
            width = sz(3); %Actual width in pixels
            subSampleX = linspace(min(x(:)),max(x(:)),width);

            subSampleY = interp1q(x,y,transpose(subSampleX));
            set(this.hPlot,'XData',subSampleX,'YData',subSampleY);
        end

    end
end

そしてユースケース:

function TestALotOfPoints()
    x = rand(10000,1);
    y = rand(10000,1);
    x = sort(x);
    ip = InterpolatedPlot(x,y,'color','r','LineWidth',2);

end
于 2012-02-05T19:56:57.330 に答える
4

小さなプロットに 1000000 個のデータ ポイントを収めることはできません。10000 点ごとに 1 点を選んでプロットしてみてはどうでしょうか。

大きなベクトルを呼び出しimresizeて縮小することを検討できますが、ポイントの 99% を省略して手動でベクトルを構築する方が高速な場合があります。

@memyself サンプリング操作は既に行われています。Matlab は、グラフに含めるデータを選択しています。なぜ matlab を信頼するのですか? あなたが示したグラフは、データを大幅に誤って表しているように見えます。密な領域は、信号が一定の値であることを示す必要がありますが、グラフでは、信号が半分の時間でその値であることを意味する可能性があります-または、そのピクセルに対応する間隔中に少なくとも 1 回その値でしたか?

于 2012-02-05T21:05:16.950 に答える
4

最大限のパフォーマンスが必要なため、最小限の OpenGL ビューアーを作成することを検討する必要があります。すべてのポイントをファイルにダンプし、MATLAB で "system" コマンドを使用してビューアーを起動します。ビューアは非常にシンプルです。Mac OS X用にコンパイルされたGLUTを使用して実装されたものを次に示します。コードはクロスプラットフォームであるため、言及したすべてのプラットフォーム用にコンパイルできるはずです。ニーズに合わせてこのビューアーを簡単に調整できるはずです。

このビューアーを MATLAB とより緊密に統合できれば、ファイルへの書き込みと読み取りを行う必要がなくなる可能性があります (= 更新が大幅に高速化されます)。しかし、私はその問題について経験がありません。おそらく、このコードを mex ファイルに入れることができますか?

編集: CPU メモリ ポインターからライン ストリップを描画するようにコードを更新しました。

// On Mac OS X, compile using: g++ -O3 -framework GLUT -framework OpenGL glview.cpp
// The file "input" is assumed to contain a line for each point:
// 0.1 1.0
// 5.2 3.0
#include <vector>
#include <sstream>
#include <fstream>
#include <iostream>
#include <GLUT/glut.h>
using namespace std;
struct float2 { float2() {} float2(float x, float y) : x(x), y(y) {} float x, y; };
static vector<float2> points;
static float2 minPoint, maxPoint;
typedef vector<float2>::iterator point_iter;
static void render() {
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(minPoint.x, maxPoint.x, minPoint.y, maxPoint.y, -1.0f, 1.0f);
    glColor3f(0.0f, 0.0f, 0.0f);
    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(2, GL_FLOAT, sizeof(points[0]), &points[0].x);
    glDrawArrays(GL_LINE_STRIP, 0, points.size());
    glDisableClientState(GL_VERTEX_ARRAY);
    glutSwapBuffers();
}
int main(int argc, char* argv[]) {
    ifstream file("input");
    string line;
    while (getline(file, line)) {
        istringstream ss(line);
        float2 p;
        ss >> p.x;
        ss >> p.y;
        if (ss)
            points.push_back(p);
    }
    if (!points.size())
        return 1;
    minPoint = maxPoint = points[0];
    for (point_iter i = points.begin(); i != points.end(); ++i) {
        float2 p = *i;
        minPoint = float2(minPoint.x < p.x ? minPoint.x : p.x, minPoint.y < p.y ? minPoint.y : p.y);
        maxPoint = float2(maxPoint.x > p.x ? maxPoint.x : p.x, maxPoint.y > p.y ? maxPoint.y : p.y);
    }
    float dx = maxPoint.x - minPoint.x;
    float dy = maxPoint.y - minPoint.y;
    maxPoint.x += dx*0.1f; minPoint.x -= dx*0.1f;
    maxPoint.y += dy*0.1f; minPoint.y -= dy*0.1f;
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
    glutInitWindowSize(512, 512);
    glutCreateWindow("glview");
    glutDisplayFunc(render);
    glutMainLoop();
    return 0;
}

編集:以下の議論に基づく新しいコードです。それぞれが 100k ポイントを含む 20 個の vbo で構成される sin 関数をレンダリングします。レンダリングされたフレームごとに 10k の新しいポイントが追加されます。これで合計2Mポイントになります。パフォーマンスは私のラップトップでリアルタイムです。

// On Mac OS X, compile using: g++ -O3 -framework GLUT -framework OpenGL glview.cpp
#include <vector>
#include <sstream>
#include <fstream>
#include <iostream>
#include <cmath>
#include <iostream>
#include <GLUT/glut.h>
using namespace std;
struct float2 { float2() {} float2(float x, float y) : x(x), y(y) {} float x, y; };
struct Vbo {
    GLuint i;
    Vbo(int size) { glGenBuffersARB(1, &i); glBindBufferARB(GL_ARRAY_BUFFER, i); glBufferDataARB(GL_ARRAY_BUFFER, size, 0, GL_DYNAMIC_DRAW); } // could try GL_STATIC_DRAW
    void set(const void* data, size_t size, size_t offset) { glBindBufferARB(GL_ARRAY_BUFFER, i); glBufferSubData(GL_ARRAY_BUFFER, offset, size, data); }
    ~Vbo() { glDeleteBuffers(1, &i); }
};
static const int vboCount = 20;
static const int vboSize = 100000;
static const int pointCount = vboCount*vboSize;
static float endTime = 0.0f;
static const float deltaTime = 1e-3f;
static std::vector<Vbo*> vbos;
static int vboStart = 0;
static void addPoints(float2* points, int pointCount) {
    while (pointCount) {
        if (vboStart == vboSize || vbos.empty()) {
            if (vbos.size() >= vboCount+2) { // remove and reuse vbo
                Vbo* first = *vbos.begin();
                vbos.erase(vbos.begin());
                vbos.push_back(first);
            }
            else { // create new vbo
                vbos.push_back(new Vbo(sizeof(float2)*vboSize));
            }
            vboStart = 0;
        }

        int pointsAdded = pointCount;

        if (pointsAdded + vboStart > vboSize)
            pointsAdded = vboSize - vboStart;

        Vbo* vbo = *vbos.rbegin();
        vbo->set(points, pointsAdded*sizeof(float2), vboStart*sizeof(float2));

        pointCount -= pointsAdded;
        points += pointsAdded;
        vboStart += pointsAdded;
    }
}
static void render() {
    // generate and add 10000 points
    const int count = 10000;
    float2 points[count];
    for (int i = 0; i < count; ++i) {
        float2 p(endTime, std::sin(endTime*1e-2f));
        endTime += deltaTime;
        points[i] = p;
    }
    addPoints(points, count);
    // render
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(endTime-deltaTime*pointCount, endTime, -1.0f, 1.0f, -1.0f, 1.0f);
    glColor3f(0.0f, 0.0f, 0.0f);
    glEnableClientState(GL_VERTEX_ARRAY);
    for (size_t i = 0; i < vbos.size(); ++i) {
        glBindBufferARB(GL_ARRAY_BUFFER, vbos[i]->i);
        glVertexPointer(2, GL_FLOAT, sizeof(float2), 0);

        if (i == vbos.size()-1)
            glDrawArrays(GL_LINE_STRIP, 0, vboStart);
        else
            glDrawArrays(GL_LINE_STRIP, 0, vboSize);
    }
    glDisableClientState(GL_VERTEX_ARRAY);
    glutSwapBuffers();
    glutPostRedisplay();
}
int main(int argc, char* argv[]) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
    glutInitWindowSize(512, 512);
    glutCreateWindow("glview");
    glutDisplayFunc(render);
    glutMainLoop();
    return 0;
}
于 2012-02-10T23:39:44.483 に答える
4

多くの人が回答で言及しているように、それほど多くの点をプロットする必要はありません。Andrey のコメントを繰り返すことが重要だと思います。

それは大量のポイントです!その量をプロットするのに十分なピクセルが画面にありません。

プロット ルーチンを異なる言語で書き直すのは時間の無駄です。MATLAB の作成に膨大な時間が費やされましたが、なぜ (合理的な時間で) 大幅に高速なプロット ルーチンを作成できると思いますか? あなたのルーチンはあまり一般的ではないかもしれませんが、そのため MATLAB コードが実行するチェックの一部が削除されますが、"ボトルネック" は、非常に多くのデータをプロットしようとしていることです。

次の 2 つの行動方針のいずれかを強くお勧めします。

  • データのサンプリング: Figure に 20 x 1000000 ポイントは必要ありません。人間の目はすべてのポイントを区別できないため、時間の無駄です。たとえば、データをビニングしてみてください。

  • 画面上のこれらすべてのポイントが必要であると主張する場合は、別のツールを使用することをお勧めします。VisItまたはParaViewは、頭に浮かぶ 2 つの例です。これらは、非常に大きなデータセットを処理するように設計された並列視覚化プログラムです (VisIt がペタバイトのデータを含むデータセットを処理するのを見たことがあります)。

于 2012-02-05T21:35:30.073 に答える
3

別のアーキテクチャを使用することは可能でしょうか?たとえば、MATLABを使用してデータを生成し、高速ライブラリまたはアプリケーション(GNUplot?)を使用してプロットを処理しますか?

プロッタがデータを消費するときに、MATLABにデータをストリームに書き込ませることも可能です。次に、MATLABがデータを生成すると、プロットが更新されます。

このアプローチは、MATLABの途方もなく遅いプロットを回避し、2つの別々のプロセス間で作業を分割します。当然のことながら、OS/CPUはおそらくプロセスを異なるコアに割り当てます。

于 2012-01-20T19:36:13.513 に答える
1

私は何年も前(2004年?)に非常に似たようなことをしました。キロヘルツでサンプリングされた生体信号をリアルタイムで表示するためのオシロスコープのようなディスプレイが必要でした。元の質問ほど多くのポイントはありませんが、MATLAB が単独で処理するには多すぎます。IIRC グラフを表示する Java コンポーネントを作成することになりました。

他の人が示唆しているように、私もデータをダウンサンプリングすることになりました。X 軸の各ピクセルについて、データの最小値と最大値を計算し、それらの値の間に短い垂直線を引きました。グラフ全体は一連の短い垂直線で構成され、それぞれがすぐ隣り合っていました。

実際、実装では、bitblt を使用して継続的にスクロールするビットマップにグラフを書き込むことになったと思います。新しいポイントのみが描画されます...または、ビットマップが静的で、ビューポートがそれに沿ってスクロールした可能性があります...とにかく長い少し前のことで、記憶が定かではないかもしれません。

于 2013-05-21T13:30:11.850 に答える
1

可能だと思いますが、プロット コード (少なくとも使用する部分) をゼロから作成する必要がある可能性があります。

実現可能性をテストするために、Win32 GUI が MEX から動作することをテストすることから始めて (call MessageBox)、独自のウィンドウを作成し、ウィンドウ メッセージが WndProc に届くことをテストします。すべてが終わったら、OpenGL コンテキストをそれにバインド (または単に GDI を使用) し、プロットを開始できます。

ただし、節約は、スレッド化ではなく、より単純なプロット コードと、VBO などの新しい OpenGL 機能の使用から得られる可能性があります。GPU ではすべてが既に並列処理されており、スレッドが増えても、GPU へのコマンド/データの転送が速くなりません。

于 2012-01-20T19:04:50.627 に答える
0

Blockquote EDIT4:これはプロットするポイントが非常に多いことは知っていますが、問題が簡単だとは決して言いませんでした。ブロッククォートを実際にプロットする前に、どのポイントが重要であるかを評価する方法がないため、特定のデータポイントを除外することはできません。

これは正しくありません。どのポイントを除外するかを知る方法があります。Matlabはすでにそれを行っています。これをどのように解決しても、ある時点で何かがそれをしなければならないでしょう。問題を「プロットするポイントをどのように決定するか」にリダイレクトする必要があると思います。

スクリーンショットに基づくと、データは波形のように見えます。あなたは大胆さのコードを見たいかもしれません。オープンソースのオーディオ編集プログラムです。波形をリアルタイムで表すプロットが表示され、最も低いスクリーンショットのものとスタイルが同じに見えます。あなたはそれらからいくつかのサンプリング技術を借りることができます。

于 2012-02-08T23:47:53.017 に答える
0

あなたが探しているのは、MEX ファイルの作成です。

私が説明するよりも、MATLAB (MEX-Files) から呼び出し可能な C/C++ および Fortran プログラムの作成(MathWorks のドキュメンテーション記事)を読むことで、おそらくより多くのメリットが得られるでしょう。

お役に立てれば。

于 2012-02-10T08:52:01.023 に答える