Можно ли ускорить построение Matlab, вызывая код C / C ++ в Matlab? - PullRequest
14 голосов
/ 20 января 2012

Обычно очень просто вызывать mex файлы (написанные на c / c ++) в Matlab для ускорения определенных вычислений.Однако, по моему опыту, истинным узким местом в Matlab является построение графиков данных.Создание дескрипторов чрезвычайно дорого, и даже если вы обновляете только данные дескрипторов (например, XData, YData, ZData), это может занять много лет.Хуже того, поскольку Matlab является однопоточной программой, невозможно обновить несколько графиков одновременно.

Поэтому мой вопрос : Можно ли написать графический интерфейс Matlabи вызвать C ++ (или какой-либо другой распараллеливаемый код), который позаботится о графике / визуализации? Я ищу кроссплатформенное решение, которое будет работать на Windows, Mac и Linux, но любое решение, которое поможет мне начатьв любой из ОС очень признателен!

Я нашел библиотеку C ++ , которая, кажется, использует синтаксис Matlab plot(), но я не уверен, что это ускорит процесс, так как ябоюсь, что если я построю график в окне figure() Матлаба, все может снова замедлиться.

Буду признателен за любые комментарии и отзывы людей, которые раньше сталкивались с подобной ситуацией!

РЕДАКТИРОВАТЬ: очевидно, я уже профилировал свой код, и узким местом является построение графиков (дюжина панелей с большим количеством данных).

EDIT2: чтобы вы могли получить награду, мне нужен реальный жизненный пример с минимальным рабочим опытом, как это сделать - наводящие на размышления ответы мне не помогут.

EDIT3: относительно данныхпостроить: в самом простом случае, подумайте о 20 линейных графиках, которые нужно обновлять каждую секунду примерно 1000000 точками данных.

EDIT4: Я знаю, что это огромное количество точек для построения, но я никогда не говорил, что проблема была легкой.Я не могу просто пропустить определенные точки данных, потому что нет способа оценить, какие точки важны, прежде чем на самом деле строить их (данные отбираются с временным разрешением менее 1 мс).Фактически, мои данные получены с помощью коммерческой системы сбора данных, которая поставляется с программой просмотра данных (написанной на c ++).Эта программа без проблем визуализирует до 60 линейных графиков с более чем 1000000 точками данных.

РЕДАКТИРОВАТЬ5: Мне не нравится, где идет текущее обсуждение.Я знаю, что субдискретизация моих данных может ускорить процесс, но это не вопрос.Здесь вопрос заключается в том, как заставить интерфейс ac / c ++ / python / java работать с matlab, чтобы, как мы надеемся, ускорить построение графиков, напрямую обращаясь к оборудованию (или используя любой другой прием / способ)

Ответы [ 9 ]

7 голосов
/ 05 февраля 2012

Пробовали ли вы тривиальное решение изменить метод рендеринга на OpenGL?

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

Внимание!В этом режиме будут некоторые вещи, которые исчезнут, и он будет выглядеть несколько иначе, но обычно графики будут работать на намного быстрее, особенно если у вас есть аппаратный ускоритель.

КстатиВы уверены, что на самом деле вы получите увеличение производительности?Например, по моему опыту, WPF графика в C# значительно медленнее, чем Matlabs, особенно точечные диаграммы и круги.

Edit : я думал о том, что числоточки, которые на самом деле нарисованы на экране, не могут быть такими большими.В основном это означает, что вам нужно интерполировать в местах, где есть пиксель на экране.Проверьте этот объект:

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
4 голосов
/ 11 февраля 2012

Поскольку вам нужна максимальная производительность, вам следует подумать о написании минимального средства просмотра OpenGL. Скопируйте все точки в файл и запустите программу просмотра, используя команду «system» в MATLAB. Зритель может быть действительно простым. Вот один из них, реализованный с использованием GLUT, скомпилированный для Mac OS X. Код является кросс-платформенным, поэтому вы сможете скомпилировать его для всех платформ, которые вы упомянули. Должно быть легко настроить эту программу для просмотра.

Если вам удастся более тесно интегрировать эту программу просмотра с MATLAB, вам, возможно, удастся избежать необходимости писать и читать из файла (= гораздо более быстрые обновления). Тем не менее, я не опытный в этом вопросе. Возможно, вы можете поместить этот код в mex-файл?

РЕДАКТИРОВАТЬ: я обновил код, чтобы нарисовать линейную полосу из указателя памяти процессора.

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

РЕДАКТИРОВАТЬ: Вот новый код, основанный на обсуждении ниже. Он выполняет функцию sin, состоящую из 20 vbos, каждый из которых содержит 100k точек. 10k новых точек добавляются в каждый визуализированный кадр. Это составляет в общей сложности 2 млн. Баллов. Производительность в режиме реального времени на моем ноутбуке.

// 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;
}
4 голосов
/ 06 февраля 2012

Как указали многие люди в своих ответах, вам не нужно наносить столько точек . Я думаю, что важно повторить комментарий Андрея:

это ОГРОМНОЕ количество очков! На экране недостаточно пикселей, чтобы отобразить эту сумму.

Переписывание процедур печати на разных языках - пустая трата времени. Огромное количество часов ушло на написание MATLAB. Почему вы думаете, что можете написать значительно более быструю процедуру построения графиков (за приемлемое количество времени)? Хотя ваша процедура может быть менее общей и, следовательно, удалит некоторые проверки, которые будет выполнять код MATLAB, ваше «узкое место» заключается в том, что вы пытаетесь отобразить так много данных.

Я настоятельно рекомендую один из двух вариантов действий:

  • Выборка данных : вам не нужно 20 х 1000000 точек на фигуре - человеческий глаз не сможет различить все точки, поэтому это пустая трата времени , Попробуйте связать ваши данные, например.

  • Если вы считаете, что вам нужны все эти точки на экране, я бы предложил использовать другой инструмент. VisIt или ParaView - это два примера, которые приходят на ум. Это параллельные программы визуализации, предназначенные для обработки чрезвычайно больших наборов данных (я видел, как VisIt обрабатывал наборы данных, содержащие данные PetaBytes).

4 голосов
/ 06 февраля 2012

Невозможно разместить 1000000 точек данных на небольшом участке.Как насчет того, чтобы выбрать один из каждых 10000 точек и построить их?

Вы можете назвать imresize на большом векторе, чтобы уменьшить его, но создание вектора вручную с пропуском 99% точек может быть быстрее.

@ memyself Операции выборки уже выполняются.Matlab выбирает, какие данные включить в график.Почему вы доверяете Matlab?Мне кажется, что график, который вы показали, значительно искажает данные.Плотные области должны указывать, что сигнал имеет постоянное значение, но на вашем графике это может означать, что сигнал имеет это значение в половину времени - или было при этом значении хотя бы один раз в течение интервала, соответствующего этому пикселю?*

3 голосов
/ 20 января 2012

Можно ли использовать альтернативную архитектуру?Например, использовать MATLAB для генерации данных и использовать быструю библиотеку или приложение (GNUplot?) Для обработки графиков?

Возможно даже, что MATLAB сможет записать данные в поток, так как плоттер использует данные.Затем график будет обновляться по мере того, как MATLAB генерирует данные.

Этот подход позволит избежать смехотворно медленного построения графика MATLAB и разделит работу между двумя отдельными процессами.ОС / ЦП, вероятно, будут назначать процесс разным ядрам как само собой разумеющееся.

1 голос
/ 21 мая 2013

Я сделал очень похожую вещь много-много лет назад (2004?).Мне нужен был дисплей, похожий на осциллограф, для биологических сигналов с выборкой в ​​килогерцах, отображаемых в реальном времени.Не так много вопросов, как в первоначальном вопросе, но все же слишком много, чтобы MATLAB мог справиться сам по себе.IIRC Я закончил тем, что написал компонент Java для отображения графика.

Как и предлагали другие люди, я также закончил сглаживание данных.Для каждого пикселя на оси x я рассчитал минимальное и максимальное значения, взятые данными, а затем нарисовал короткую вертикальную линию между этими значениями.Весь граф состоял из последовательности коротких вертикальных линий, каждая из которых непосредственно примыкала к следующей.

На самом деле, я думаю, что реализация закончилась тем, что записал график в растровое изображение, которое непрерывно прокручивалось с использованием bitblt, при этом рисовались только новые точки ... или, возможно, растровое изображение было статическим, а область просмотра прокручивалась вдоль него ...во всяком случае, это было давно, и я, возможно, не помню это правильно.

1 голос
/ 20 января 2012

Я думаю, что это возможно, но, вероятно, потребует написания кода для черчения (по крайней мере, частей, которые вы используете) с нуля, поскольку все, что вы можете использовать повторно, именно то, что замедляет вас.

Чтобы проверить выполнимость, я бы начал с тестирования того, что любой Win32 GUI работает от MEX (вызов MessageBox), а затем приступил к созданию собственного окна, чтобы проверить, что сообщения окна поступают в ваш WndProc. Как только все это произойдет, вы можете привязать к нему контекст OpenGL (или просто использовать GDI) и начать строить графики.

Однако экономия, скорее всего, будет достигнута за счет более простого построения кода и использования более новых функций OpenGL, таких как VBO, а не многопоточности. В GPU все уже параллельно, и больше потоков не помогают быстрее передавать команды / данные в GPU.

0 голосов
/ 10 февраля 2012

То, что вы ищете, - это создание MEX-файла.

Вместо того, чтобы объяснять это, вы, вероятно, получите больше пользы от прочтения этого: Создание программ на C / C ++ и Fortran, которые можно вызывать из MATLAB (MEX-файлы) (статья документации от MathWorks).

Надеюсь, это поможет.

0 голосов
/ 09 февраля 2012

Цитата EDIT4: я знаю, что это огромное количество точек, но я никогда не говорил, что проблема была легкой.Я не могу просто пропустить определенные точки данных, потому что нет способа оценить, какие точки важны, прежде чем на самом деле составить их график.Есть способ узнать, какие моменты пропустить.Matlab уже делает это.Что-то должно будет сделать это в какой-то момент, независимо от того, как вы решите это.Я думаю, что вам нужно перенаправить вашу проблему так: «Как мне определить, какие точки я должен построить?»Возможно, вы захотите взглянуть на код смелости.Это программа для редактирования аудио с открытым исходным кодом.Он отображает графики для представления формы волны в режиме реального времени, и они выглядят по стилю идентичными изображению на самом низком снимке экрана.Вы можете позаимствовать у них некоторые методы отбора проб.

...