简体   繁体   English

是否有可能通过在matlab中调用c / c ++代码来加速matlab绘图?

[英]is it possible to speed-up matlab plotting by calling c / c++ code in matlab?

It is generally very easy to call mex files (written in c/c++) in Matlab to speed up certain calculations. 在Matlab中调用mex文件(用c / c ++编写)通常很容易加速某些计算。 In my experience however, the true bottleneck in Matlab is data plotting. 然而,根据我的经验,Matlab的真正瓶颈是数据绘图。 Creating handles is extremely expensive and even if you only update handle data (eg, XData, YData, ZData), this might take ages. 创建句柄非常昂贵,即使您只更新句柄数据(例如,XData,YData,ZData),这可能需要很长时间。 Even worse, since Matlab is a single threaded program, it is impossible to update multiple plots at the same time. 更糟糕的是,由于Matlab是单线程程序,因此无法同时更新多个图。

Therefore my question : Is it possible to write a Matlab GUI and call C++ (or some other parallelizable code) which would take care of the plotting / visualization? 因此我的问题 是否有可能编写一个Matlab GUI并调用C ++(或其他一些可并行化的代码)来处理绘图/可视化? I'm looking for a cross-platform solution that will work on Windows, Mac and Linux, but any solution that get's me started on either OS is greatly appreciated! 我正在寻找一个可以在Windows,Mac和Linux上运行的跨平台解决方案,但任何让我开始使用任何操作系统的解决方案都非常感谢!

I found a C++ library that seems to use Matlab's plot() syntax but I'm not sure whether this would speed things up, since I'm afraid that if I plot into Matlab's figure() window, things might get slowed down again. 我找到了一个似乎使用Matlab的plot()语法的C ++库 ,但我不确定这是否会加快速度,因为我害怕如果我在Matlab的figure()窗口中绘图,事情可能会再次放慢速度。

I would appreciate any comments and feedback from people who have dealt with this kind of situation before! 我很感激以前处理过这种情况的人的任何意见和反馈!

EDIT: obviously, I've already profiled my code and the bottleneck is the plotting (dozen of panels with lots of data). 编辑:显然,我已经描述了我的代码,瓶颈是绘图(十几个包含大量数据的面板)。

EDIT2: for you to get the bounty, I need a real life, minimal working example on how to do this - suggestive answers won't help me. EDIT2:为了获得赏金,我需要一个真实的生活,最简单的工作实例如何做到这一点 - 暗示性的答案对我没有帮助。

EDIT3: regarding the data to plot: in a most simplistic case, think about 20 line plots, that need to be updated each second with something like 1000000 data points. EDIT3:关于要绘制的数据:在最简单的情况下,考虑20个线图,需要每秒更新一次,如1000000个数据点。

EDIT4: I know that this is a huge amount of points to plot but I never said that the problem was easy. 编辑4:我知道这是一个巨大的积分,但我从来没有说过这个问题很简单。 I can not just leave out certain data points, because there's no way of assessing what points are important, before actually plotting them (data is sampled a sub-ms time resolution). 我不能忽略某些数据点,因为在实际绘制它们之前无法评估哪些点是重要的(数据采样时间为亚毫秒时间分辨率)。 As a matter of fact, my data is acquired using a commercial data acquisition system which comes with a data viewer (written in c++). 事实上,我的数据是使用商业数据采集系统获得的,该系统附带数据查看器(用c ++编写)。 This program has no problem visualizing up to 60 line plots with even more than 1000000 data points. 该程序可以直观地显示多达60个线图,甚至超过1000000个数据点。

EDIT5: I don't like where the current discussion is going. 编辑5:我不喜欢当前讨论的目的。 I'm aware that sub-sampling my data might speeds up things - however, this is not the question. 我知道对我的数据进行二次采样可能会加快速度 - 然而,这不是问题所在。 The question here is how to get ac / c++ / python / java interface to work with matlab in order hopefully speed up plotting by talking directly to the hardware (or using any other trick / way) 这里的问题是如何让ac / c ++ / python / java接口与matlab一起工作,以期通过直接与硬件交谈(或使用任何其他技巧/方式)加快绘图速度

Did you try the trivial solution of changing the render method to OpenGL ? 您是否尝试过将渲染方法更改为OpenGL的简单解决方案?

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

Warning! 警告! There will be some things that disappear in this mode, and it will look a bit different, but generally plots will runs much faster, especially if you have a hardware accelerator. 会有一些事情,在这种模式下消失,它会看起来有点不同,但一般地块将运行更快,尤其是如果你有一个硬件加速器。

By the way, are you sure that you will actually gain a performance increase? 顺便问一下,你确定你会真正获得性能提升吗? For example, in my experience, WPF graphics in C# are considerably slower than Matlabs, especially scatter plot and circles. 例如,根据我的经验, C#中的WPF图形比Matlabs慢得多,尤其是散点图和圆形。

Edit : I thought about the fact that the number of points that is actually drawn to the screen can't be that much. 编辑 :我想到了实际绘制到屏幕上的点数不会那么多。 Basically it means that you need to interpolate at the places where there is a pixel in the screen. 基本上,这意味着您需要在屏幕中有像素的位置进行插值。 Check out this object: 看看这个对象:

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

And here is an example how to use it: 以下是如何使用它的示例:

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

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

end

Another possible improvement: Also, if your x data is sorted, you can use interp1q instead of interp , which will be much faster. 另一种可能的改进:另外,如果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

And the use case: 用例:

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

end

There is no way you can fit 1000000 data points on a small plot. 你无法在一个小地块上安装1000000个数据点。 How about you choose one in every 10000 points and plot those? 你如何选择每10000分中的一个并绘制那些?

You can consider calling imresize on the large vector to shrink it, but manually building a vector by omitting 99% of the points may be faster. 您可以考虑在大型矢量上调用imresize来缩小它,但是通过省略99%的点来手动构建矢量可能会更快。

@memyself The sampling operations are already occurring. @memyself采样操作已经发生。 Matlab is choosing what data to include in the graph. Matlab正在选择要包含在图表中的数据。 Why do you trust matlab? 你为什么信任matlab? It looks to me that the graph you showed significantly misrepresents the data. 在我看来,您展示的图表显着错误地表示了数据。 The dense regions should indicate that the signal is at a constant value, but in your graph it could mean that the signal is at that value half the time - or was at that value at least once during the interval corresponding to that pixel? 密集区域应指示信号处于恒定值,但在图表中,它可能意味着信号处于该值的一半时间 - 或者在对应于该像素的间隔期间至少一次该值?

As a number of people have mentioned in their answers, you do not need to plot that many points . 正如许多人在答案中提到的那样, 你不需要绘制那么多积分 I think it is important to rpeat Andrey's comment: 我认为重复安德烈的评论很重要:

that is a HUGE amount of points! 这是一个巨大的积分! There isn't enough pixels on the screen to plot that amount. 屏幕上没有足够的像素来绘制该数量。

Rewriting plotting routines in different languages is a waste of your time. 用不同语言重写绘图程序是浪费你的时间。 A huge number of hours have gone into writing MATLAB, whay makes you think you can write a significantly faster plotting routine (in a reasonable amount of time)? 编写MATLAB已经花费了大量的时间,whay让你觉得你可以编写一个明显更快的绘图程序(在合理的时间内)? Whilst your routine may be less general, and therefore would remove some of the checks that the MATLAB code will perform, your "bottleneck" is that you are trying to plot so much data. 虽然您的例程可能不那么通用,因此会删除MATLAB代码将执行的一些检查,但您的“瓶颈”是您试图绘制如此多的数据。

I strongly recommend one of two courses of action: 我强烈建议采取以下两种行动方案之一:

  • Sample your data : You do not need 20 x 1000000 points on a figure - the human eye won't be able to distinguish between all the points, so it is a waste of time. 对数据进行采样 :数字上不需要20 x 1000000点 - 人眼无法区分所有点,因此浪费时间。 Try binning your data for example. 例如,尝试将数据分级。

  • If you maintain that you need all those points on the screen, I would suggest using a different tool. 如果你认为你需要屏幕上的所有这些点,我建议使用不同的工具。 VisIt or ParaView are two examples that come to mind. VisItParaView是我想到的两个例子。 They are parallel visualisation programs designed to handle extremenly large datasets (I have seen VisIt handle datasets that contained PetaBytes of data). 它们是用于处理极大数据集的并行可视化程序(我已经看到包含PetaBytes数据的VisIt句柄数据集)。

Since you want maximum performance you should consider writing a minimal OpenGL viewer. 由于您希望获得最佳性能,因此您应该考虑编写最小的OpenGL查看器。 Dump all the points to a file and launch the viewer using the "system"-command in MATLAB. 将所有点转储到文件并使用MATLAB中的“system”命令启动查看器。 The viewer can be really simple. 观众可以非常简单。 Here is one implemented using GLUT, compiled for Mac OS X. The code is cross platform so you should be able to compile it for all the platforms you mention. 这是使用GLUT实现的,为Mac OS X编译。代码是跨平台的,因此您应该能够为您提到的所有平台编译它。 It should be easy to tweak this viewer for your needs. 根据您的需要调整此查看器应该很容易。

If you are able to integrate this viewer more closely with MATLAB you might be able to get away with not having to write to and read from a file (= much faster updates). 如果你能够将这个查看器与MATLAB更紧密地集成在一起,你可能无需写入和读取文件(=更快的更新)。 However, I'm not experienced in the matter. 但是,我没有这方面的经验。 Perhaps you can put this code in a mex-file? 也许您可以将此代码放在mex文件中?

EDIT: I've updated the code to draw a line strip from a CPU memory pointer. 编辑:我已更新代码从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;
}

EDIT: Here is new code based on the discussion below. 编辑:这是基于以下讨论的新代码。 It renders a sin function consisting of 20 vbos, each containing 100k points. 它呈现由20个vbos组成的sin函数,每个函数包含100k个点。 10k new points are added each rendered frame. 每个渲染帧添加10k个新点。 This makes a total of 2M points. 这使得总共2M点。 The performance is real-time on my laptop. 我的笔记本电脑上的性能是实时的。

// 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;
}

Would it be possible to use an alternate architectue? 是否可以使用备用架构? For example, use MATLAB to generate the data and use a fast library or application (GNUplot?) to handle the plotting? 例如,使用MATLAB生成数据并使用快速库或应用程序(GNUplot?)来处理绘图?

It might even be possible to have MATLAB write the data to a stream as the plotter consumes the data. 甚至可能让MATLAB在绘图仪消耗数据时将数据写入流。 Then the plot would be updated as MATLAB generates the data. 然后,当MATLAB生成数据时,将更新绘图。

This approach would avoid MATLAB's ridiculously slow plotting and divide the work up between two separate processes. 这种方法可以避免MATLAB在两个独立的进程之间进行可笑的缓慢绘图和划分工作。 The OS/CPU would probably assign the process to different cores as a matter of course. OS / CPU可能会将进程分配给不同的核心。

I did a very similar thing many many years ago (2004?). 许多年前(2004年?)我做了很多类似的事情。 I needed an oscilloscope-like display for kilohertz sampled biological signals displayed in real time. 我需要一个类似示波器的显示器来实时显示千赫兹采样的生物信号。 Not quite as many points as the original question has, but still too many for MATLAB to handle on its own. 没有原始问题那么多,但对于MATLAB来说仍然有太多不能自行处理。 IIRC I ended up writing a Java component to display the graph. IIRC我最终编写了一个Java组件来显示图形。

As other people have suggested, I also ended up down-sampling the data. 正如其他人所建议的那样,我也最终对数据进行了下采样。 For each pixel on the x-axis, I calculated the minimum and maximum values taken by the data, then drew a short vertical line between those values. 对于x轴上的每个像素,我计算了数据所采用的最小值和最大值,然后在这些值之间画了一条短的垂直线。 The entire graph consisted of a sequence of short vertical lines, each immediately adjacent to the next. 整个图由一系列短垂直线组成,每条垂直线紧邻下一条。

Actually, I think that the implementation ended up writing the graph to a bitmap that scrolled continuously using bitblt, with only new points being drawn ... or maybe the bitmap was static and the viewport scrolled along it ... anyway it was a long time ago and I might not be remembering it right. 实际上,我认为实现最终将图形写入一个使用bitblt连续滚动的位图,只绘制了新点...或者位图是静态的,视口也沿着它滚动...无论如何它是一个长的时间以前,我可能没有记住它。

I think it's possible, but likely to require writing the plotting code (at least the parts you use) from scratch, since anything you could reuse is exactly what's slowing you down. 我认为这是可能的,但可能需要从头开始编写绘图代码(至少是你使用的部分),因为任何你可以重复使用的东西正在减慢你的速度。

To test feasibility, I'd start with testing that any Win32 GUI works from MEX (call MessageBox ), then proceed to creating your own window, test that window messages arrive to your WndProc. 为了测试可行性,我首先测试任何Win32 GUI都可以从MEX(调用MessageBox )工作,然后继续创建自己的窗口,测试窗口消息到达你的WndProc。 Once all that's going, you can bind an OpenGL context to it (or just use GDI), and start plotting. 完成所有操作后,您可以将OpenGL上下文绑定到它(或者只使用GDI),然后开始绘图。

However, the savings is likely to come from simpler plotting code and use of newer OpenGL features such as VBOs, rather than threading. 但是,节省的成本可能来自更简单的绘图代码和使用更新的OpenGL功能(如VBO),而不是线程。 Everything is already parallel on the GPU, and more threads don't help transfer of commands/data to the GPU any faster. 一切都已经在GPU上并行,更多线程无法更快地帮助将命令/数据传输到GPU。

Blockquote EDIT4: I know that this is a huge amount of points to plot but I never said that the problem was easy. Blockquote EDIT4:我知道这是一个很大的积分,但我从来没有说过这个问题很简单。 I can not just leave out certain data points, because there's no way of assessing what points are important, before actually plotting them Blockquote 我不能忽略某些数据点,因为在实际绘制它们之前无法评估哪些点很重要Blockquote

This is incorrect. 这是不正确的。 There is a way to to know which points to leave out. 有一种方法可以知道遗漏哪些要点。 Matlab is already doing it. Matlab已经在做了。 Something is going to have to do it at some point no matter how you solve this. 无论你怎么解决这个问题,都必须在某些方面做到这一点。 I think you need to redirect your problem to be "how do I determine which points I should plot?". 我认为您需要将问题重定向为“我如何确定应该绘制哪些点?”。

Based on the screenshot, the data looks like a waveform. 根据屏幕截图,数据看起来像波形。 You might want to look at the code of audacity. 您可能想要查看大胆的代码。 It is an open source audio editing program. 它是一个开源音频编辑程序。 It displays plots to represent the waveform in real time, and they look identical in style to the one in your lowest screen shot. 它显示实时表示波形的图表,它们的样式与最低屏幕截图中的样式相同。 You could borrow some sampling techniques from them. 你可以从中借用一些采样技术。

What you are looking for is the creation of a MEX file. 您正在寻找的是创建MEX文件。

Rather than me explaining it, you would probably benefit more from reading this: Creating C/C++ and Fortran Programs to be Callable from MATLAB (MEX-Files) (a documentation article from MathWorks). 而不是我解释它,你可能会从阅读中获益更多: 创建可从MATLAB(MEX文件)调用的C / C ++和Fortran程序 (MathWorks的文档文章)。

Hope this helps. 希望这可以帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM