是否可以通过在 matlab 中调用 c/c++ 代码来加速 matlab 绘图?
Posted
技术标签:
【中文标题】是否可以通过在 matlab 中调用 c/c++ 代码来加速 matlab 绘图?【英文标题】:is it possible to speed-up matlab plotting by calling c / c++ code in matlab? 【发布时间】:2012-02-15 07:37:05 【问题描述】:在 Matlab 中调用 mex
文件(用 c/c++ 编写)来加速某些计算通常非常容易。然而,根据我的经验,Matlab 的真正瓶颈是数据绘图。创建句柄非常昂贵,即使您只更新句柄数据(例如 XData、YData、ZData),这也可能需要很长时间。更糟糕的是,由于 Matlab 是单线程程序,不可能同时更新多个绘图。
因此我的问题:是否可以编写一个 Matlab GUI 并调用 C++(或其他一些可并行化的代码)来处理绘图/可视化?我我正在寻找一个可以在 Windows、Mac 和 Linux 上运行的跨平台解决方案,但非常感谢任何能让我在任一操作系统上入门的解决方案!
我发现了一个 C++ library 似乎使用 Matlab 的 plot()
语法,但我不确定这是否会加快速度,因为我担心如果我在 Matlab 的 figure()
窗口中绘图,事情可能会变得又放慢了速度。
如果以前处理过这种情况的人提供任何 cmet 和反馈,我将不胜感激!
编辑:显然,我已经分析了我的代码,瓶颈是绘图(有大量数据的数十个面板)。
EDIT2:为了让您获得赏金,我需要一个真实的、最小的工作示例来说明如何做到这一点 - 暗示性的答案对我没有帮助。
EDIT3:关于要绘制的数据:在最简单的情况下,考虑 20 条线图,每秒需要更新大约 1000000 个数据点。
EDIT4:我知道这是要绘制的大量点,但我从未说过这个问题很容易。我不能只遗漏某些数据点,因为在实际绘制它们之前无法评估哪些点是重要的(数据以亚毫秒时间分辨率采样)。事实上,我的数据是使用带有数据查看器(用 C++ 编写)的商业数据采集系统获取的。该程序可以毫无问题地可视化多达 60 个线图,甚至超过 1000000 个数据点。
EDIT5:我不喜欢当前讨论的进展。我知道对我的数据进行二次采样可能会加快速度 - 但是,这不是问题所在。这里的问题是如何让 c / c++ / python / java 接口与 matlab 一起工作,以希望通过直接与硬件对话(或使用任何其他技巧/方式)来加快绘图速度
【问题讨论】:
仅供参考:我找到了一个建议使用散点图而不是绘图的链接:mathworks.com/matlabcentral/answers/2529-figure-plot-slow。顺便说一句,有没有人知道为什么 MATLAB 和 Octave 中的图形与软件包中的其他功能相比如此缓慢? 是否有机会获取数据以查看瓶颈在哪里? @memyself,这是一个巨大的积分!屏幕上没有足够的像素来绘制该数量。也许您应该通过数学运算来简化数据,例如对每 1000 个点进行线性拟合,并绘制稀疏点集。 (有点像 3D 图形) @memyself,你不需要知道要拿什么积分。只需对屏幕中像素的 x 位置进行插值。当然,您需要为缩放案例找出一些东西。请查看我的更新答案。 这个问题太离谱了。正如许多人所提到的,实际上不可能在屏幕上绘制超过 2,000 个数据点。实际上,在全屏模式下,您可以清晰绘制的点数可能在 500 左右。当然,如果 OP 真的打算同时显示 20 个地块,这个数字就会下降。所以唯一的问题是您使用什么算法来决定对哪些点进行采样。 OP 应该决定一个标准来确定“重要”点,例如异常值并绘制它们。 【参考方案1】:我认为这是可能的,但可能需要从头开始编写绘图代码(至少是您使用的部分),因为您可以重复使用的任何内容都会拖慢您的速度。
为了测试可行性,我首先测试任何 Win32 GUI 是否可以在 MEX 上运行(调用 MessageBox
),然后继续创建您自己的窗口,测试窗口消息是否到达您的 WndProc。完成后,您可以将 OpenGL 上下文绑定到它(或仅使用 GDI),然后开始绘图。
但是,节省可能来自更简单的绘图代码和使用更新的 OpenGL 功能(如 VBO),而不是线程。 GPU 上的一切都已经是并行的,更多的线程无助于更快地将命令/数据传输到 GPU。
【讨论】:
谢谢,我会调查的!您的解决方案似乎是特定于 Windows 的,但我想类似的解决方案应该适用于 MacOS 和 Linux,对吧? @memyself:是的。我对那里使用的功能不太熟悉,但同样的方法应该可以工作。不过,我更喜欢 ahoffer 的想法,需要的代码要少得多。【参考方案2】:是否可以使用替代架构?例如,使用 MATLAB 生成数据并使用快速库或应用程序(GNUplot?)来处理绘图?
当绘图仪使用数据时,甚至可以让 MATLAB 将数据写入流。然后绘图将随着 MATLAB 生成数据而更新。
这种方法可以避免 MATLAB 极其缓慢的绘图,并将工作分配给两个独立的进程。 OS/CPU 可能会理所当然地将进程分配给不同的内核。
【讨论】:
你有如何做到这一点的例子吗?我特别好奇你将如何实现 matlab 和外部库之间的通信。图书馆会创建和管理自己的窗口吗?现在我的 matlab 程序带有一个精致的 GUI,因为我需要与传入的数据进行交互。因此我的想法是只替换我的 GUI 的核心绘图。 我在考虑基于文件的 IPC。通常,MATLAB 创建数据,然后使用“绘图”命令绘制它们。无需调用 plot,而是将 MATLAB 数据写入文件。创建一个等待文件关闭然后运行 Gnuplot 的小例程。该例程告诉 Gnuplot 使用命令行参数将该文件用作绘图数据。我不是一个出色的脚本程序员,但该例程可能会用脚本语言或 shell 脚本编写。有关其他类型 IPC 的列表,请查看此处:en.wikipedia.org/wiki/Interprocess_communication。 PS:基于文件的 IPC 和管道也可用于共享数据,无需等待文件或管道关闭。 @memyself,IPC 的一个想法 - 您还可以使用 Matlab NE Builder 将您的 Matlab 代码编译为 COM 服务器,并在您的 C++ 应用程序中使用它。【参考方案3】:您是否尝试过将渲染方法更改为OpenGL
的简单解决方案?
opengl hardware;
set(gcf,'Renderer','OpenGL');
警告! 在这种模式下,有些东西会消失,看起来会有些不同,但通常绘图会运行得快得多,尤其是如果您有硬件加速器。
顺便问一下,你确定你真的会获得性能提升吗?
例如,根据我的经验,C#
中的WPF
图形比 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
【讨论】:
结果不够好。看看下面的截图:i.imgur.com/6VBId.png 上面板是您使用子采样的绘图函数,下面板是我的绘图函数,基本上绘制每个数据点。 @memyself, 2 建议:(1) 将点数从width
增加到 2*width
或分配给 subSampleX 的任何内容 (2) 如果您需要,将线宽增加到 3想要得到填充效果。
@memyself,顺便说一句,就原始速度而言,现在够快了吗?
速度很差。如果我绘制这 253193 个数据点中的每一个, tic;plot();toc 给我 0.013264 秒。您的绘图功能需要 0.034375 秒。
这里有一个想法:interp1
不是更多地用于从更粗略的样本集中获取更密集的数据集,然后在这里进行采样吗?如何通过获取每个像素桶的第一个/最小/最大/最后一个点并绘制它们来进行采样?应该产生一个与原始图非常相似的图,保留所有峰值。【参考方案4】:
您无法在一个小图上拟合 1000000 个数据点。您如何在每 10000 个点中选择一个并绘制它们?
您可以考虑在大向量上调用imresize
来缩小它,但是通过省略 99% 的点手动构建向量可能会更快。
@memyself 采样操作已经开始。 Matlab 正在选择要包含在图中的数据。你为什么信任matlab?在我看来,您显示的图表严重歪曲了数据。密集区域应表明信号处于恒定值,但在您的图表中,这可能意味着信号在一半时间内处于该值 - 或者在对应于该像素的间隔期间至少处于该值一次?
【讨论】:
这不是这里的任务。我需要绘制所有点,因为我无法提前确定哪些点可以省略,哪些点很重要。 没有办法在屏幕上“打包”那么多点!根本没有足够的像素密度。在某些时候,会进行这样的评估,以确定当只有约 100 个空间时如何拟合 1000000 个数据点。如果您自己进行此评估,您将受益。我也遇到过类似的问题,这就是解决方案。尝试使用 imresize 看看它的外观。【参考方案5】:正如许多人在回答中提到的那样,您不需要绘制那么多点。我认为重复安德烈的评论很重要:
这是一个巨大的积分!屏幕上没有足够的像素来绘制该数量。
用不同的语言重写绘图例程是在浪费您的时间。编写 MATLAB 已经花费了大量时间,为什么您认为您可以编写一个明显更快的绘图程序(在合理的时间内)?虽然您的例程可能不太通用,因此会删除 MATLAB 代码将执行的一些检查,但您的“瓶颈”是您试图绘制如此多的数据。
我强烈推荐两种行动方案之一:
对您的数据进行采样:您不需要一个图形上的 20 x 1000000 个点 - 人眼无法区分所有点,因此浪费时间。例如,尝试对您的数据进行分箱。
如果您坚持需要屏幕上的所有这些点,我建议您使用不同的工具。 VisIt 或 ParaView 是我想到的两个例子。它们是并行可视化程序,旨在处理超大型数据集(我见过 VisIt 处理包含 PB 级数据的数据集)。
【讨论】:
没问题。我从您的问题中得知您是否尝试过该答案中建议的技术?另外,您“相信用 C++ 或 C 进行绘图有可能加快速度”,但您不认为 MATLAB 开发人员也想到了这一点吗?最终,MATLAB 是用 C(和 Java)编写的。 @memyself,这种“填充效果”可以通过使用数据中的密度测量人为地进行。请考虑再次将数据上传到网络,这样对您有帮助会容易得多。 @memyself 恐怕这并不能说明每一点都很重要。告诉我你可以从你的时间序列中获得哪些你无法从安德烈那里获得的信息。来自***:“数据分析是一个检查、清理、转换和建模数据的过程,其目标是突出有用信息、提出结论并支持决策制定”。如果您要分析这两个时间序列,您会发现它们具有相同(至少相似)的均值、标准差、频率分量等。 顺便说一句,我在这里猜测了一下,因为在您链接到您的图像上没有显示 y 轴刻度。另外,我赞同安德烈的最后一条评论。 @memyself,正如 Chris 所说,您应该了解您的数据的性质以及您想要查看的内容。是音频波吗?如果是这样,请考虑咨询音频专家。【参考方案6】:块引用 EDIT4:我知道这是要绘制的大量点,但我从未说过这个问题很容易。我不能只遗漏某些数据点,因为在实际绘制它们之前无法评估哪些点是重要的 块引用
这是不正确的。有一种方法可以知道要省略哪些点。 Matlab 已经在这样做了。无论你如何解决这个问题,在某个时候都必须做一些事情。我认为您需要将您的问题重定向为“我如何确定应该绘制哪些点?”。
根据屏幕截图,数据看起来像一个波形。你可能想看看大胆的代码。它是一个开源的音频编辑程序。它显示实时表示波形的图,它们的样式与最低屏幕截图中的样式相同。你可以借鉴他们的一些采样技术。
【讨论】:
【参考方案7】:您正在寻找的是 MEX 文件的创建。
比起我解释它,您可能会从阅读以下内容中受益更多:Creating C/C++ and Fortran Programs to be Callable from MATLAB (MEX-Files)(来自 MathWorks 的文档文章)。
希望这会有所帮助。
【讨论】:
一个 mex 文件不是解决方案,因为如果我仍然需要调用这个 mex 文件 20 次(20 线图),并且由于 matlab 只是一个单线程程序,绘图将会发生顺序而不是并行。然后我基本上回到了 matlab 速度(无论如何都使用 opengl) 我不确定细节,但我认为使用 MEX 文件将 a) 允许从文件创建线程,并且 b) 允许 C++ 和MATLAB 代码。【参考方案8】:由于您希望获得最高性能,您应该考虑编写一个最小的 OpenGL 查看器。将所有点转储到文件并使用 MATLAB 中的“system”命令启动查看器。查看器可以非常简单。这是一个使用 GLUT 实现的,为 Mac OS X 编译的。代码是跨平台的,所以你应该能够为你提到的所有平台编译它。根据您的需要调整此查看器应该很容易。
如果您能够将此查看器与 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;
编辑:这是基于以下讨论的新代码。它渲染了一个由 20 个 vbos 组成的 sin 函数,每个 vbos 包含 100k 个点。每个渲染帧添加 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;
【讨论】:
这绝对是我开始走的方向。然而,即时模式也是如此。请记住,我想绘制 20 条线,每条线有 1000000 个点 - 每次渲染每个数据点都太昂贵了。目前我认为在显卡上分配内存的 VBO 是要走的路。 我明白了。我以为你在渲染点。但是线路应该更快。您是否更改了每次渲染的数据?在这种情况下,我认为 vbo 不会给您带来太多性能提升。相反,您可以从 CPU 内存指针进行渲染。 数据来自记录的时间序列,具有固定的间隔 dx,例如:y = rand(1, 100000); x = 1:numel(y)
是否将每个数据点与下一个数据点连接(线图)或点是否未连接没关系。另请注意:绘制的点数将始终保持不变!您能否提供一个如何从 CPU 内存指针进行渲染的示例?
我已经更新了代码以从 CPU 内存指针中绘制一条线。
现在我对您的问题有了更多了解(您每帧都添加新点),您可以创建多个 vbo。每个人负责几千分。在队列中使用这些,以便您只需要添加到最前面的 vbo。这样可以避免每帧将所有点发送到 GPU。【参考方案9】:
很多年前(2004 年?)我做过类似的事情。我需要一个类似示波器的显示器,用于实时显示千赫兹采样的生物信号。没有原始问题那么多的点,但对于 MATLAB 自己处理的点仍然太多。 IIRC 我最终编写了一个 Java 组件来显示图表。
正如其他人所建议的那样,我最终也对数据进行了下采样。对于 x 轴上的每个像素,我计算了数据的最小值和最大值,然后在这些值之间画了一条短垂直线。整个图表由一系列短垂直线组成,每条垂直线都紧邻下一条。
实际上,我认为实现最终将图形写入位图,该位图使用 bitblt 连续滚动,只绘制新点……或者位图是静态的,视口沿着它滚动……无论如何是很久以前的事了,我可能记错了。
【讨论】:
以上是关于是否可以通过在 matlab 中调用 c/c++ 代码来加速 matlab 绘图?的主要内容,如果未能解决你的问题,请参考以下文章