Возможно ли ускорить построение matlab, вызывая код c/С++ в matlab?

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

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

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

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

EDIT:, очевидно, что я уже профилировал свой код, а узким местом является построение графика (дюжина панелей с большим количеством данных).

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

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

EDIT4: Я знаю, что это огромное количество очков, но я никогда не говорил, что проблема была простой. Я не могу просто оставить определенные точки данных, потому что нет способа оценить, какие точки важны, прежде чем на самом деле их замышлять (данные дискретизируются временным разрешением sub-ms). На самом деле мои данные получены с использованием коммерческой системы сбора данных, которая поставляется с программой просмотра данных (написанной на С++). У этой программы нет проблем с визуализацией до 60 линий с еще более чем 1000000 точками данных.

EDIT5: Мне не нравится, где идет текущее обсуждение. Я знаю, что суб-выборка моих данных может ускорить работу, но это не вопрос. Вопрос здесь в том, как получить интерфейс c/С++/python/java для работы с Matlab, чтобы, надеюсь, ускорить построение графика, разговаривая напрямую с аппаратным обеспечением (или используя любой другой трюк/способ)

Ответы

Ответ 1

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

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

EDIT: я обновил код, чтобы нарисовать строку с указателем памяти процессора.

// 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: Вот новый код, основанный на обсуждении ниже. Он выполняет функцию 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;
}

Ответ 2

Попробовал ли тривиальное решение изменить метод рендеринга на 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

Ответ 3

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

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

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

Ответ 4

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

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

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

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

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

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

Ответ 5

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

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

Этот подход позволил бы избежать MATLAB смехотворно медленного построения графика и разделить работу между двумя отдельными процессами. Разумеется, OS/CPU, вероятно, назначит процесс различным ядрам.

Ответ 6

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

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

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

Ответ 7

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

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

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

Ответ 8

Blockquote EDIT4: Я знаю, что это огромное количество очков для сюжета, но я никогда не говорил, что проблема была простой. Я не могу просто оставить определенные точки данных, потому что нет способа оценить, какие моменты важны, прежде чем на самом деле их замышлять Blockquote

Это неверно. Есть способ узнать, какие моменты оставить без внимания. Matlab уже это делает. Что-то придется делать в какой-то момент независимо от того, как вы это решаете. Я думаю, вам нужно перенаправить вашу проблему как "как определить, какие точки я должен построить?".

На основе снимка экрана данные выглядят как форма волны. Вы можете посмотреть на код смелости. Это программа для редактирования аудио с открытым исходным кодом. Он отображает графики, представляющие форму волны в реальном времени, и они выглядят одинаково по стилю тем, что находится на вашем самом низком снимке экрана. Вы могли бы взять у них некоторые методы отбора проб.