【QCustomPlot】性能提升之修改源码(版本 V2.x.x)

这篇具有很好参考价值的文章主要介绍了【QCustomPlot】性能提升之修改源码(版本 V2.x.x)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

说明

使用 QCustomPlot 绘图库辅助开发时整理的学习笔记。同系列文章目录可见 《绘图库 QCustomPlot 学习笔记》目录。QCustomPlot 是开源项目,源码编写十分规范,想要理解它的可视化思路不算特别困难。我在这篇随笔中总结一下常用的源码修改技巧,下面的每一个技巧都是独立的,相互之间不会引发任何冲突。示例中使用的 QCustomPlot 版本号为 2.0.1,但在更高的 2.x.x 版本中也适用。

目录
  • 说明
  • 1. 技巧一:启用 GPU 加速
    • 1.1 下载并编译 FreeGlut 库
    • 1.2 在 qcustomplot.cpp 文件中添加代码
    • 1.3 在 pro 文件中添加代码
    • 1.4 启用 GPU 加速
    • 1.5 加速效果
  • 2. 技巧二:添加曲线平滑功能
    • 2.1 在 qcustomplot.h 文件中添加代码
    • 2.2 在 qcustomplot.cpp 文件中添加代码
    • 2.3 启用曲线平滑
    • 2.4 平滑效果
  • 3. 技巧三:导出一维绘图数据地址
    • 3.1 一维绘图数据的内存结构
    • 3.2 在 qcustomplot.h 文件中添加代码
    • 3.3 使用绘图数据地址来更新数据
  • 4. 技巧四:导出 QCPColorMap 绘图数据地址
    • 4.1 QCPColorMap 绘图数据的内存结构
    • 4.2 在 qcustomplot.h 文件中添加代码
    • 4.3 使用绘图数据地址来更新数据

1. 技巧一:启用 GPU 加速

这里选用 FreeGlut 库。

1.1 下载并编译 FreeGlut 库

去 https://freeglut.sourceforge.net/index.php 下载 freeglut 源码,编译出 freeglut 库,编译过程不做介绍。然后将编译出来的库以及 GL 文件夹下的五个头文件都包含进项目中,我使用的是 MSVC2015 64bit 静态库,因此在 pro/pri 文件中添加以下代码(因人而异):

HEADERS += \
    $$PWD/GL/freeglut.h \
    $$PWD/GL/freeglut_ext.h \
    $$PWD/GL/freeglut_std.h \
    $$PWD/GL/freeglut_ucall.h \
    $$PWD/GL/glut.h

CONFIG(debug, debug | release) {
    LIBS += -L$$PWD/lib64 -lfreeglut_staticd
    LIBS += -L$$PWD/lib64 -lfreeglutd
}

CONFIG(release, debug | release) {
    LIBS += -L$$PWD/lib64 -lfreeglut_static
    LIBS += -L$$PWD/lib64 -lfreeglut
}

1.2 在 qcustomplot.cpp 文件中添加代码

在文件的前面几行(比如 #include "qcustomplot.h" 的后面)添加以下代码:

#define GLUT_DISABLE_ATEXIT_HACK
#include <GL/freeglut.h>

若同一个界面上有多个 QCustimPlot 窗口对象,且都开启了 GPU 加速,则在窗口切换时图形显示可能会出现错乱(被称为上下文异常),为了避免这种现象,需要在 QCPPaintBufferGlFbo::draw 函数里面添加以下代码:

/* inherits documentation from base class */
void QCPPaintBufferGlFbo::draw(QCPPainter *painter) const
{
    if (!painter || !painter->isActive())
    {
        qDebug() << Q_FUNC_INFO << "invalid or inactive painter passed";
        return;
    }
    if (!mGlFrameBuffer)
    {
        qDebug() << Q_FUNC_INFO << "OpenGL frame buffer object doesn't exist, reallocateBuffer was not called?";
        return;
    }
    
    // 这个 if 语句是新添加的
    if(QOpenGLContext::currentContext() != mGlContext.data())
    {
        mGlContext.data()->makeCurrent(mGlContext.data()->surface());
    }
    
    painter->drawImage(0, 0, mGlFrameBuffer->toImage());
}

1.3 在 pro 文件中添加代码

pro 文件中,添加以下代码:

QT       += printsupport opengl
DEFINES += QCUSTOMPLOT_USE_OPENGL

这个 printsupport 是使用 QCustomPlot 时需要添加的,不论是否启用 GPU 加速都需要添加。后面的 opengl 则是为了启用 GPU 加速而新添的,此外,还需要使用 DEFINES 添加 QCUSTOMPLOT_USE_OPENGL 宏。

1.4 启用 GPU 加速

对 QCustomPlot 对象使用 setOpenGl() 函数设置是否启用 OpenGL,如下所示:

ui->Plot->setOpenGl(true);

可以通过 openGl() 函数的返回值判断是否成功启用了 GPU 加速:

qDebug() << "启用状态" << ui->Plot->openGl();

需要注意的是,当绘制的图形有大块填充区域,尤其是半透明的填充时,GPU 加速的效果才明显,这个时候才能减轻 CPU 压力。如果仅仅绘制一些简单的曲线图还开启 OpenGL,结果往往会适得其反,CPU 压力不减反增,有兴趣的可以进行测试,打开任务管理器观察启用前后 CPU 的占用百分比即可。

1.5 加速效果

绘制实时更新的、含有填充区域的图像,未开启 GPU 加速前的效果:

【QCustomPlot】性能提升之修改源码(版本 V2.x.x)

开启 GPU 加速后的效果:

【QCustomPlot】性能提升之修改源码(版本 V2.x.x)

以上演示例中并没有更改数据刷新频率(都为 10 ms 间隔)及数据量大小(都为 100 个点),两者仅有的差别为是否调用 setOpenGl(true) 开启了 GPU 加速。从结果中可以看到,开启 OpenGL 后,CPU 占用率从 16%~17% 下降到 7%~8%,GPU 占用率从 0% 上升到 41%~43%,并且从视觉效果上看,刷新变得更快了,这可能是因为 CPU 被减轻了压力,单次计算后显示所需时间更短了。


2. 技巧二:添加曲线平滑功能

思路是先计算贝塞尔控制点,然后使用 QPainterPath 绘制平滑曲线,参考资料:

  • CodeProject-Draw-a-Smooth-Curve-through-a-Set-of-2D-Points-wit。
  • 公孙二狗 - 个人博客。
  • CSDN - 七六伍。

2.1 在 qcustomplot.h 文件中添加代码

在原生的 class QCP_LIB_DECL QCPGraph 类定义中(使用搜索功能找到对应位置)添加以下两个内容,注意 publicprotected 限定符:

class QCP_LIB_DECL QCPGraph : public QCPAbstractPlottable1D<QCPGraphData>
{
public: 
    ...
    void setSmooth(bool smooth);             // 新增内容
    
protected:
    ...
    bool mSmooth;                            // 新增内容
}

qcustomplot.h 文件的末尾(#endif 的上一行)添加 SmoothCurveGenerator 类定义的代码:

class SmoothCurveGenerator
{
protected:
    static QPainterPath generateSmoothCurveImp(const QVector<QPointF> &points) {
        QPainterPath path;
        int len = points.size();
        
        if (len < 2) {
            return path;
        }
        
        QVector<QPointF> firstControlPoints;
        QVector<QPointF> secondControlPoints;
        calculateControlPoints(points, &firstControlPoints, &secondControlPoints);
        
        path.moveTo(points[0].x(), points[0].y());
        
        // Using bezier curve to generate a smooth curve.
        for (int i = 0; i < len - 1; ++i) {
            path.cubicTo(firstControlPoints[i], secondControlPoints[i], points[i+1]);
        }
        
        return path;
    }
public:
    static QPainterPath generateSmoothCurve(const QVector<QPointF> &points) {
        QPainterPath result;
        
        int segmentStart = 0;
        int i = 0;
        int pointSize = points.size();
        while (i < pointSize) {
            if (qIsNaN(points.at(i).y()) || qIsNaN(points.at(i).x()) || qIsInf(points.at(i).y())) {
                QVector<QPointF> lineData(i - segmentStart); std::copy(points.constBegin() + segmentStart, points.constBegin() + i - segmentStart, lineData.begin());
                result.addPath(generateSmoothCurveImp(lineData));
                segmentStart = i + 1;
            }
            ++i;
        }
        QVector<QPointF> lineData(i - segmentStart); std::copy(points.constBegin() + segmentStart, points.constBegin() + i - segmentStart, lineData.begin());
        result.addPath(generateSmoothCurveImp(lineData));
        return result;
    }
    
    static QPainterPath generateSmoothCurve(const QPainterPath &basePath, const QVector<QPointF> &points) {
        if (points.isEmpty()) return basePath;
        
        QPainterPath path = basePath;
        int len = points.size();
        if (len == 1) {
            path.lineTo(points.at(0));
            return path;
        }
        
        QVector<QPointF> firstControlPoints;
        QVector<QPointF> secondControlPoints;
        calculateControlPoints(points, &firstControlPoints, &secondControlPoints);
        
        path.lineTo(points.at(0));
        for (int i = 0; i < len - 1; ++i)
            path.cubicTo(firstControlPoints[i], secondControlPoints[i], points[i+1]);
        
        return path;
    }
    
    static void calculateFirstControlPoints(double *&result, const double *rhs, int n) {
        result = new double[n];
        double *tmp = new double[n];
        double b = 2.0;
        result[0] = rhs[0] / b;
        
        // Decomposition and forward substitution.
        for (int i = 1; i < n; i++) {
            tmp[i] = 1 / b;
            b = (i < n - 1 ? 4.0 : 3.5) - tmp[i];
            result[i] = (rhs[i] - result[i - 1]) / b;
        }
        
        for (int i = 1; i < n; i++) {
            result[n - i - 1] -= tmp[n - i] * result[n - i]; // Backsubstitution.
        }
        
        delete[] tmp;
    }
    
    static void calculateControlPoints(const QVector<QPointF> &knots,
                                       QVector<QPointF> *firstControlPoints,
                                       QVector<QPointF> *secondControlPoints) {
        int n = knots.size() - 1;
        
        firstControlPoints->reserve(n);
        secondControlPoints->reserve(n);
        
        for (int i = 0; i < n; ++i) {
            firstControlPoints->append(QPointF());
            secondControlPoints->append(QPointF());
        }
        
        if (n == 1) {
            // Special case: Bezier curve should be a straight line.
            // P1 = (2P0 + P3) / 3
            (*firstControlPoints)[0].rx() = (2 * knots[0].x() + knots[1].x()) / 3;
            (*firstControlPoints)[0].ry() = (2 * knots[0].y() + knots[1].y()) / 3;
            
            // P2 = 2P1 – P0
            (*secondControlPoints)[0].rx() = 2 * (*firstControlPoints)[0].x() - knots[0].x();
            (*secondControlPoints)[0].ry() = 2 * (*firstControlPoints)[0].y() - knots[0].y();
            
            return;
        }
        
        // Calculate first Bezier control points
        double *xs = nullptr;
        double *ys = nullptr;
        double *rhsx = new double[n]; // Right hand side vector
        double *rhsy = new double[n]; // Right hand side vector
        
        // Set right hand side values
        for (int i = 1; i < n - 1; ++i) {
            rhsx[i] = 4 * knots[i].x() + 2 * knots[i + 1].x();
            rhsy[i] = 4 * knots[i].y() + 2 * knots[i + 1].y();
        }
        rhsx[0] = knots[0].x() + 2 * knots[1].x();
        rhsx[n - 1] = (8 * knots[n - 1].x() + knots[n].x()) / 2.0;
        rhsy[0] = knots[0].y() + 2 * knots[1].y();
        rhsy[n - 1] = (8 * knots[n - 1].y() + knots[n].y()) / 2.0;
        
        // Calculate first control points coordinates
        calculateFirstControlPoints(xs, rhsx, n);
        calculateFirstControlPoints(ys, rhsy, n);
        
        // Fill output control points.
        for (int i = 0; i < n; ++i) {
            (*firstControlPoints)[i].rx() = xs[i];
            (*firstControlPoints)[i].ry() = ys[i];
            
            if (i < n - 1) {
                (*secondControlPoints)[i].rx() = 2 * knots[i + 1].x() - xs[i + 1];
                (*secondControlPoints)[i].ry() = 2 * knots[i + 1].y() - ys[i + 1];
            } else {
                (*secondControlPoints)[i].rx() = (knots[n].x() + xs[n - 1]) / 2;
                (*secondControlPoints)[i].ry() = (knots[n].y() + ys[n - 1]) / 2;
            }
        }
        
        delete xs;
        delete ys;
        delete[] rhsx;
        delete[] rhsy;
    }
};

2.2 在 qcustomplot.cpp 文件中添加代码

在原生的 QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) 构造函数(使用搜索功能找到对应位置)实现中,添加 mSmooth 成员变量的初始化代码:

QCPGraph::QCPGraph(QCPAxis *keyAxis, QCPAxis *valueAxis) :
  QCPAbstractPlottable1D<QCPGraphData>(keyAxis, valueAxis)
{
    ...
    mSmooth = false;  // 新增内容
}

在对应位置添加 QCPGraph::setSmooth() 成员函数的实现(比如写在 void QCPGraph::setAdaptiveSampling(bool enabled) 的后面):

void QCPGraph::setSmooth(bool smooth)
{
    mSmooth = smooth;
}

将原生的 QCPGraph::drawLinePlot 成员函数(使用搜索功能找到对应位置)修改为如下形式,实质上只添加了个 if 语句:

void QCPGraph::drawLinePlot(QCPPainter *painter, const QVector<QPointF> &lines) const
{
    if (painter->pen().style() != Qt::NoPen && painter->pen().color().alpha() != 0)
    {
        applyDefaultAntialiasingHint(painter);
        if (mSmooth && mLineStyle == lsLine) painter->drawPath(SmoothCurveGenerator::generateSmoothCurve(lines));
        else drawPolyline(painter, lines);
    }
}

2.3 启用曲线平滑

对 QCPGraph 对象使用 setSmooth() 函数设置是否启用曲线平滑,如下所示:

ui->Plot->graph(0)->setSmooth(true);

2.4 平滑效果

绘制 50 个点,未启用曲线平滑时的效果:

【QCustomPlot】性能提升之修改源码(版本 V2.x.x)

启用曲线平滑时的效果:

【QCustomPlot】性能提升之修改源码(版本 V2.x.x)


3. 技巧三:导出一维绘图数据地址

3.1 一维绘图数据的内存结构

一维绘图数据都存储在 QCPDataContainer 这个类里面,绘图数据存储的容器为 QVector<DataType>,详见 qcustomplot.h 文件中 QCPDataContainer 的类定义。不同的一维绘图类型有着不同的底层数据类型:

  • 对于 QCPGraph 绘图类型,这个 DataTypeQCPGraphData,查看 QCPGraphData 类定义,它有且仅有两个 double 类型的成员变量 keyvalue。因此 QCPGraph 的绘图数据被存储在一块连续的内存块中(类似于 double 数组),绘图数据在内存中按顺序 x0-y0-x1-y1-x2-y2... 这样依次排列,xiyi 分别表示第 i 个横轴数据和第 i 个纵轴数据。
  • 对于 QCPCurve 绘图类型,这个 DataTypeQCPCurveData,查看 QCPCurveData 类定义,它有且仅有三个 double 类型的成员变量 tkeyvalue。因此 QCPCurve 的绘图数据在内存中按顺序 t0-x0-y0-t1-x1-y1-t2-x2-y2... 这样依次排列,这个 t 表示参数曲线对应的参变量。
  • 对于 QCPBars 绘图类型,这个 DataTypeQCPBarsData,查看 QCPBarsData 类定义,它有且仅有两个 double 类型的成员变量 keyvalue。因此 QCPBars 绘图数据与 QCPGraph 绘图数据的内存排列方式一样。
  • QCPStatisticalBoxQCPFinancial 这两个绘图类型就相对复杂些,但不变的是,绘图数据仍被依次存储在一块连续的内存块中,感兴趣的可以看下 QCPStatisticalBoxDataQCPFinancialData 的类定义。

更新一维绘图数据时,QCustomPlot 提供了一些接口,分别为:

// QCPGraph 4个接口
void setData(QSharedPointer<QCPGraphDataContainer> data)
void setData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false)
void addData(const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false)
void addData(double key, double value)
    
// QCPCurve 7个接口
void setData(QSharedPointer<QCPCurveDataContainer> data)
void setData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false)
void setData(const QVector<double> &keys, const QVector<double> &values)
void addData(const QVector<double> &t, const QVector<double> &keys, const QVector<double> &values, bool alreadySorted=false)
void addData(const QVector<double> &keys, const QVector<double> &values)
void addData(double t, double key, double value)
void addData(double key, double value)
    
// QCPBars 4个接口
void setData(QSharedPointer<QCPBarsDataContainer > data)
void setData(const QVector< double > &keys, const QVector<double> &values, bool alreadySorted=false)
void addData(const QVector< double > &keys, const QVector<double> &values, bool alreadySorted=false)
void addData(double key, double value)
    
// QCPStatisticalBox 4个接口
void setData(QSharedPointer<QCPStatisticalBoxDataContainer> data)
void setData(const QVector<double> &keys, const QVector<double> &minimum, const QVector<double> &lowerQuartile, const QVector<double> &median, const QVector<double> &upperQuartile, const QVector<double> &maximum, bool alreadySorted=false)
void addData(const QVector<double> &keys, const QVector<double> &minimum, const QVector<double> &lowerQuartile, const QVector<double> &median, const QVector<double> &upperQuartile, const QVector<double> &maximum, bool alreadySorted=false)
void addData(double key, double minimum, double lowerQuartile, double median, double upperQuartile, double maximum, const QVector<double> &outliers=QVector<double>())
    
// QCPFinancial 4个接口
void setData(QSharedPointer<QCPFinancialDataContainer> data)
void setData(const QVector<double> &keys, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close, bool alreadySorted=false)
void addData(const QVector<double> &keys, const QVector<double> &open, const QVector<double> &high, const QVector<double> &low, const QVector<double> &close, bool alreadySorted=false)
void addData(double key, double open, double high, double low, double close)

除第一个接口外,原生的 setData()addData() 接口内部都会调用 QVector 相关的 resize()size()std::sort()std::inplace_merge() 等函数,还存在很多 if 语句。在一些时候,特别是数据点数固定但数值更新速率很高时,频繁的调用 size() 等函数会大大延长刷新时间,此时接口中的很多操作都是不必要的,因此不妨直接将存储绘图数据的 QVector<DataType> 容器地址交给使用者,以获得更佳的性能,缩短更新时间。QCustomPlot 提供了以下几个函数来访问绘图数据的 QVector<DataType> 容器(就是下面的 mData),可通过这些函数的联合使用来修改绘图数据:

// QCPDataContainer
int size() const { return mData.size()-mPreallocSize; }
iterator begin() { return mData.begin()+mPreallocSize; }
iterator end() { return mData.end(); }

// QCPGraph
QSharedPointer<QCPGraphDataContainer> data() const { return mDataContainer; }

// QCPCurve
QSharedPointer<QCPCurveDataContainer> data() const { return mDataContainer; }

// QCPBars
QSharedPointer<QCPBarsDataContainer> data() const { return mDataContainer; }

// QCPStatisticalBox
QSharedPointer<QCPStatisticalBoxDataContainer> data() const { return mDataContainer; }

// QCPFinancial
QSharedPointer<QCPFinancialDataContainer> data() const { return mDataContainer; }

另一种方法是在源码中添加一行代码,直接导出 mData 地址,获得更自由的控制权。

3.2 在 qcustomplot.h 文件中添加代码

QCPDataContainer 类定义的 public 区域,添加以下一行代码即可:

template <class DataType>
class QCPDataContainer // no QCP_LIB_DECL, template class ends up in header (cpp included below)
{
public:
    ...
        
    // 新添内容
    QVector<DataType>* coreData() { return &mData; }
}

3.3 使用绘图数据地址来更新数据

对相应的绘图对象使用 coreData() 函数获得绘图数据的地址,如下所示:

QVector<QCPGraphData> *mData = ui->Plot->graph(0)->data()->coreData();

得到这个地址后,就可以用数组访问的方式逐点更新数据,或者使用 memcpy() 做一次更新。后面绘图时会默认数据已经排好了序,不会再进行排序操作,因此若需要重排数据顺序,需人工提前排好。

// 可能需要预分配容器内存,预分配内存仅需一次
mData->reserve(totalSize);
mData->resize(totalSize);

// 逐点更新 xi = 5.0;
(*mData)[i].key = 5.0;

// 逐点更新 yi = sin(5.0);
(*mData)[i].value = sin(5.0);

// 一次更新
memcpy((char*)mData, (char*)pData, sizeof(double)*totalSize*2);

注意:使用 memcpy() 一次更新时,这个 pData 为存储新数据的内存首地址,pData 所指空间中数据的排列方式必须和对应绘图数据的内存排列方式保持一致。


4. 技巧四:导出 QCPColorMap 绘图数据地址

4.1 QCPColorMap 绘图数据的内存结构

QCPColorMap 绘图数据存储在 QCPColorMapData 这个类里面,详见 qcustomplot.h 文件中 QCPColorMapData 的类定义,绘图数据存储的容器为一维 double 数组,按行进行存储,纵坐标小的排在数组前面。纵坐标最小的一行排在数组最前面,纵坐标最大的一行排在数组最后面;存储每行时,横坐标最小的排在数组前面,横坐标最大的排在数组后面。QCustomPlot 提供的数据更新接口有:

// QCPColorMapData
void setData(double key, double value, double z)
void setCell(int keyIndex, int valueIndex, double z)
void fill(double z)
    
// QCPColorMap
void setData(QCPColorMapData *data, bool copy=false)

同样在数据点数固定但数值更新速率很高时,原生接口中的很多操作都是不必要的。对于 QCPColorMap,QCustomPlot 没有提供绘图数据容器迭代器的接口,只能通过在源码中添加代码的方式得到绘图数据容器的地址。

4.2 在 qcustomplot.h 文件中添加代码

QCPColorMapData 类定义的 public 区域,添加以下一行代码即可:

class QCP_LIB_DECL QCPColorMapData
{
public:
    ...

    // 新添内容
    double *coreData() { mDataModified = true; return mData; }
}

4.3 使用绘图数据地址来更新数据

对 QCPColorMap 对象使用 coreData() 函数获得绘图数据的地址,如下所示:

double *mData = m_pColorMap->data()->coreData();

得到这个地址后,就可以用数组访问的方式逐点更新数据,或者使用 memcpy() 做一次更新。

// 不要在外部使用 new 来分配内存,而应使用原生接口来做内存预分配
m_pColorMap->data()->setSize(xsize, ysize);

// 逐点更新 m[xi][yj] = 5.0; 其中 xi,yj 为非负整型索引值
mData[(yj-1)*xsize+xi] = 5.0;

// 一次更新
memcpy((char*)mData, (char*)pData, sizeof(double)*xsize*ysize);

注意:使用 memcpy() 一次更新时,这个 pData 为存储新数据的内存首地址,pData 所指空间中数据的排列方式必须和 QCPColorMap 绘图数据的内存排列方式保持一致。文章来源地址https://www.toymoban.com/news/detail-463720.html

到了这里,关于【QCustomPlot】性能提升之修改源码(版本 V2.x.x)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • golang操作excel的高性能库——excelize/v2

    Excelize是一个纯Go编写的库,提供了一组功能,允许你向XLAM / XLSM / XLSX / XLTM / XLTX文件写入和读取。支持读取和写入由Microsoft Excel™ 2007及更高版本生成的电子表格文档。通过高度兼容性支持复杂组件,并提供了流式API,用于生成或从包含大量数据的工作表中读取数据。此库需

    2024年02月12日
    浏览(46)
  • 1.UnityProfiler性能分析提升性能

    1.main thread 主线程 业务逻辑都在这里,我们调用Unity API都在这里;例如设置transform位置,main thread里面处理 2.render thread,渲染线程,负责渲染图像、执行渲染循环、处理GPU命令、帧同步。 3.  这个则表示当前负载最多可以绘制多少次(当前帧数) 4.Batches :批次,绘制场景,

    2024年01月25日
    浏览(60)
  • 性能优化实践:一行代码性能提升几十倍?

    Part1 问题背景 在一般的互联网公司,大家都非常忙碌。活儿是永远干不完的。这时候,我建议先做重要的事情。试想:一个人永远都在做「紧急不重要」的事情,他的产出必然是非常低的。这就是为什么「重要不紧急」在第二象限,仅仅排在「重要且紧急」后面。 所以对于

    2024年04月28日
    浏览(40)
  • VS2022 C++修改Window系统DNS源代码V2.0

    这是自己使用VS2022 C++编写开发的Window系统下修改DNS脚本程序第2个版本,适合Win10系统和Win7系统。cfg.txt文件存放要修改的DNS,最多4个。 详细源代码如下: setdns.cpp

    2024年02月11日
    浏览(54)
  • ES性能优化最佳实践- 检索性能提升30倍!

            Elasticsearch是被广泛使用的搜索引擎技术,它的应用领域远不止搜索引擎,还包括日志分析、实时数据监控、内容推荐、电子商务平台、企业级搜索解决方案以及许多其他领域。其强大的全文搜索、实时索引、分布式性能和丰富的插件生态系统使其成为了许多不同

    2024年02月08日
    浏览(53)
  • 记录Chrome插件从V2版本升级到V3版本的过程中遇到的问题

    总结一下自己在把Chrome V2版本的插件升级到V3版本的过程中,遇到的一些问题,之前也有发布一章V3版本的manifest.json配置项参数说明,基本也涵盖了下面提到的几个配置项的改动,传送门 总结分了两大块,一块是manifest配置文件V2和V3有哪几个配置项不同,一块是升级过程遇到

    2023年04月08日
    浏览(94)
  • java微信小程序支付V2版本(亲测有效)

    MCHID(商户号) 就是商户注册之后,微信支付给你的唯一的数字 APPID(小程序的appid) SSLFILE(微信支付需要申请证书,这个字段就是把申请的证书下载之后,存放在你服务器的某个文件夹的路径) NOTIFYURL(支付成功之后,微信会给你这个url发送一条支付成功的消息) APIKEY(证书的秘钥

    2024年02月09日
    浏览(43)
  • 接口性能提升方案

    1.索引 2.sql优化 3.远程调用多个服务,串行调用completeFuture,并行调用,需要定义线程池,并发情况,线程创建过多,方案二,数据异构,放缓存 详细参考: CompletableFuture 详解(一):基本概念及用法_tong_master的博客-CSDN博客 4.循环调用,改批量,500条 5.异步处理,不重要逻

    2023年04月10日
    浏览(31)
  • Elasticsearch:提升 Elasticsearch 性能

    Elasticsearch 是为你的用户提供无缝搜索体验的不可或缺的工具。 在最近的 QCon 会议上,我遇到了很多的开发者。在他们的系统中,Elastic Stack 是不可缺少的工具,无论在搜索,可观测性或安全领域,Elastic Stack 都发挥着巨大的作用。我们在手机中常见的应用或者网站上的搜索基

    2023年04月08日
    浏览(36)
  • .NET MAUI 性能提升

    .NET多平台应用程序UI (MAUI)将android、iOS、macOS和Windows API统一为一个API,这样你就可以编写一个应用程序在许多平台上本机运行。我们专注于提高您的日常生产力以及您的应用程序的性能。我们认为,开发人员生产率的提高不应该以应用程序性能为代价。 应用程序的大小也是如

    2024年02月07日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包