C++继承虚表对效率的影响

上篇文章中发现内存生生循环居然比使用空间索引速度还快,昨儿睡前想到之前与同事讨论过且网上查过的一个问题:继承类函数虚表调用导致的效率下降,于是今儿写了点测试代码,以Envelope的Intersect方法作为测试内容,对比了结构体、类、虚基类、派生类的函数调用效率


对比方法

测试数据

郑州市矢量数据
数据类型:Line
数据量:1554304条
x_min:454250.000000
x_max:479750.045600
y_min:3839499.634654
y_max:3862000.032000
空间参考:Xian_1980_3_Degree_GK_CM_114E

对比逻辑

打开上述数据,将外包矩形存入结构体,以不同结构体/对象组织方式为一个测试函数,进行容器插入、以范围R遍历数据n次求交、容器清理作为一次完整流程,记录插入、查询、清理时间作为横向对比参考。重复如上操作t次,取平均值。
其中
R:x_min:465701.923
R:x_max:469994.306
R:y_min:3852567.173
R:y_max:3855717.229
n:100
t:5

测试代码

#include "stdafx.h"
#include "CompIndex_Spatial.h"

#include 
using namespace std;

#include "shapefil.h"

class CEnvelope;
class IEnvelope;
class Envelope;

struct stEnvelope
{
    stEnvelope(size_t i, double x1, double x2, double y1, double y2)
    {
        id = i;
        x_min = x1;
        x_max = x2;
        y_min = y1;
        y_max = y2;
    }
    bool intersect(stEnvelope *pOther)
    {
        return x_min <= pOther->x_max &&
            x_max >= pOther->x_min &&
            y_min <= pOther->y_max &&
            y_max >= pOther->y_min;
    }
    size_t id;
    double x_min;
    double x_max;
    double y_min;
    double y_max;
};
typedef list   OriginEnvelopes;

class CEnvelope
{
public:
    CEnvelope(size_t i, double x1, double x2, double y1, double y2)
    {
        id = i;
        x_min = x1;
        x_max = x2;
        y_min = y1;
        y_max = y2;
    }
    ~CEnvelope(){}

public:
    bool intersect(CEnvelope *pOther)
    {
        return x_min <= pOther->x_max &&
            x_max >= pOther->x_min &&
            y_min <= pOther->y_max &&
            y_max >= pOther->y_min;
    }

private:
    size_t id;
    double x_min;
    double x_max;
    double y_min;
    double y_max;
};

class IEnvelope
{
public:
    virtual bool intersect(IEnvelope *pOther) = 0;

};

class Envelope : public IEnvelope
{
public:
    Envelope(size_t i, double x1, double x2, double y1, double y2)
    {
        id = i;
        x_min = x1;
        x_max = x2;
        y_min = y1;
        y_max = y2;
    }

public:
    virtual bool intersect(IEnvelope *pOther)
    {
        Envelope *pEnvlp = dynamic_cast(pOther);
        if (!pEnvlp)
            return false;

        return x_min <= pEnvlp->x_max &&
            x_max >= pEnvlp->x_min &&
            y_min <= pEnvlp->y_max &&
            y_max >= pEnvlp->y_min;
    }

private:
    size_t id;
    double x_min;
    double x_max;
    double y_min;
    double y_max;
};

static IEnvelope *  CreateEnvelope(size_t i, double x1, double x2, double y1, double y2)
{
    Envelope *pEnvelope = new Envelope(i,x1,x2,y1,y2);
    return pEnvelope;
}
static void         DestroyEnvelope(IEnvelope *pEnvelope)
{
    if (pEnvelope)
    {
        delete pEnvelope;
        pEnvelope = NULL;
    }
}


bool init_data(OriginEnvelopes &envlps);
void free_data(OriginEnvelopes &envlps);

void Loop1(const OriginEnvelopes &envlps);  // list<结构体>存储,直接取成员
void Loop2(const OriginEnvelopes &envlps);  // list<类>存储,函数调用
void Loop3(const OriginEnvelopes &envlps);  // list<派生类>存储,虚函数调用
void Loop4(const OriginEnvelopes &envlps);  // list<虚接口>存储,虚基类调用

bool init_data(OriginEnvelopes &envlps)
{
    string strShpPath = g_strDataDir + g_strDataset + ".shp";
    SHPHandle hSHP = SHPOpen(strShpPath.c_str(), "rb");
    if (NULL == hSHP)
        return false;

    SHPSetFastModeReadObject(hSHP, true);
    int nEntities(0),nShapeType(0);
    double padfMinBound[4],padfMaxBound[4];
    SHPGetInfo(hSHP, &nEntities, &nShapeType, padfMinBound, padfMaxBound);
    for (int i=0; idfXMin,pObj->dfXMax, pObj->dfYMin,pObj->dfYMax);
        envlps.push_back(pEnvlp);

        SHPDestroyObject(pObj);
    }
    SHPClose(hSHP);

    return true;
}

void free_data(OriginEnvelopes &envlps)
{
    for (OriginEnvelopes::iterator iter=envlps.begin(); iter!=envlps.end(); ++iter)
    {
        delete (*iter);
        (*iter) = NULL;
    }
    envlps.clear();
}

const int c_iQueryTimes = 100;

// list<结构体>存储,无函数调用
void Loop1(const OriginEnvelopes &envlps)
{
    clock_t tBeg,tEnd;
    int iQueryCnt(0);
    string strCurr("Loop1");

    typedef OriginEnvelopes Envelopes;
    Envelopes envelopes;

    // 1.insert
    tBeg = clock();
    for (OriginEnvelopes::const_iterator iter=envlps.begin(); iter!=envlps.end(); ++iter)
    {
        stEnvelope *pEnvlp = new stEnvelope((*iter)->id, (*iter)->x_min, (*iter)->x_max, (*iter)->y_min, (*iter)->y_max);
        envelopes.push_back(pEnvlp);
    }
    tEnd = clock();
    printf("%s:insert:%d\n", strCurr.c_str(), tEnd-tBeg);

    // 2.query
    tBeg = clock();
    stEnvelope qEnvelope(-1, g_x_min,g_x_max, g_y_min,g_y_max);
    for (int iVal=0; iValintersect(&qEnvelope))
            {
                ++iQueryCnt;
            }
        }
    }
    tEnd = clock();
    printf("%s:query:%d\n", strCurr.c_str(), tEnd-tBeg);

    // 3.clean
    tBeg = clock();
    for (Envelopes::iterator iter=envelopes.begin(); iter!=envelopes.end(); ++iter)
    {
        delete (*iter);
        (*iter) = NULL;
    }
    envelopes.clear();
    tEnd = clock();
    printf("%s:clean:%d\n", strCurr.c_str(), tEnd-tBeg);
}

// list<类>存储,函数调用
void Loop2(const OriginEnvelopes &envlps)
{
    clock_t tBeg,tEnd;
    int iQueryCnt(0);
    string strCurr("Loop2");

    typedef list Envelopes;
    Envelopes envelopes;

    // 1.insert
    tBeg = clock();
    for (OriginEnvelopes::const_iterator iter=envlps.begin(); iter!=envlps.end(); ++iter)
    {
        CEnvelope *pEnvlp = new CEnvelope((*iter)->id, (*iter)->x_min, (*iter)->x_max, (*iter)->y_min, (*iter)->y_max);
        envelopes.push_back(pEnvlp);
    }
    tEnd = clock();
    printf("%s:insert:%d\n", strCurr.c_str(), tEnd-tBeg);

    // 2.query
    CEnvelope qEnvelope(-1, g_x_min,g_x_max, g_y_min,g_y_max);
    tBeg = clock();
    for (int iVal=0; iValintersect(&qEnvelope))
            {
                ++iQueryCnt;
            }
        }
    }
    tEnd = clock();
    printf("%s:query:%d\n", strCurr.c_str(), tEnd-tBeg);

    // 3.clean
    tBeg = clock();
    for (Envelopes::iterator iter=envelopes.begin(); iter!=envelopes.end(); ++iter)
    {
        delete (*iter);
        (*iter) = NULL;
    }
    envelopes.clear();
    tEnd = clock();
    printf("%s:clean:%d\n", strCurr.c_str(), tEnd-tBeg);
}

// list<派生类>存储,虚函数调用
void Loop3(const OriginEnvelopes &envlps)
{
    clock_t tBeg,tEnd;
    int iQueryCnt(0);
    string strCurr("Loop3");

    typedef list Envelopes;
    Envelopes envelopes;

    // 1.insert
    tBeg = clock();
    for (OriginEnvelopes::const_iterator iter=envlps.begin(); iter!=envlps.end(); ++iter)
    {
        Envelope *pEnvlp = new Envelope((*iter)->id, (*iter)->x_min, (*iter)->x_max, (*iter)->y_min, (*iter)->y_max);
        envelopes.push_back(pEnvlp);
    }
    tEnd = clock();
    printf("%s:insert:%d\n", strCurr.c_str(), tEnd-tBeg);

    // 2.query
    Envelope qEnvelope(-1, g_x_min,g_x_max, g_y_min,g_y_max);
    tBeg = clock();
    for (int iVal=0; iValintersect(&qEnvelope))
            {
                ++iQueryCnt;
            }
        }
    }
    tEnd = clock();
    printf("%s:query:%d\n", strCurr.c_str(), tEnd-tBeg);

    // 3.clean
    tBeg = clock();
    for (Envelopes::iterator iter=envelopes.begin(); iter!=envelopes.end(); ++iter)
    {
        delete (*iter);
        (*iter) = NULL;
    }
    envelopes.clear();
    tEnd = clock();
    printf("%s:clean:%d\n", strCurr.c_str(), tEnd-tBeg);
}

// list<虚基类>存储,虚函数调用
void Loop4(const OriginEnvelopes &envlps)
{
    clock_t tBeg,tEnd;
    int iQueryCnt(0);
    string strCurr("Loop4");

    typedef list Envelopes;
    Envelopes envelopes;

    // 1.insert
    tBeg = clock();
    for (OriginEnvelopes::const_iterator iter=envlps.begin(); iter!=envlps.end(); ++iter)
    {
        IEnvelope *pEnvlp = CreateEnvelope((*iter)->id, (*iter)->x_min, (*iter)->x_max, (*iter)->y_min, (*iter)->y_max);
        envelopes.push_back(pEnvlp);
    }
    tEnd = clock();
    printf("%s:insert:%d\n", strCurr.c_str(), tEnd-tBeg);

    // 2.query
    Envelope qEnvelope(-1, g_x_min,g_x_max, g_y_min,g_y_max);
    tBeg = clock();
    for (int iVal=0; iValintersect(&qEnvelope))
            {
                ++iQueryCnt;
            }
        }
    }
    tEnd = clock();
    printf("%s:query:%d\n", strCurr.c_str(), tEnd-tBeg);

    // 3.clean
    tBeg = clock();
    for (Envelopes::iterator iter=envelopes.begin(); iter!=envelopes.end(); ++iter)
    {
        DestroyEnvelope(*iter);
        (*iter) = NULL;
    }
    envelopes.clear();
    tEnd = clock();
    printf("%s:clean:%d\n", strCurr.c_str(), tEnd-tBeg);
}

void TestLoopEfficiency()
{
    OriginEnvelopes envlps;
    init_data(envlps);

    Loop1(envlps);
    Loop2(envlps);
    Loop3(envlps);
    Loop4(envlps);

    free_data(envlps);
    printf("\n");
}

对比结果


insert query clean info
Loop1 197 904 123 list<结构体>存储,函数调用
Loop2 188 897 126 list<类>存储,函数调用
Loop3 185 4540 132 list<派生类>存储,虚函数调用
Loop4 186 4564 132 list<虚基类>存储,虚函数调用

1与2结果基本一致,3与4的结果基本一致
但查询函数是否为虚函数的运算效率差距为5倍左右
该差距比例会视调用函数的运算密集程度而变,参见另外一篇关于虚继承效率的帖子:

http://blog.csdn.net/hengyunabc/article/details/7461919


回到开篇的问题,由于上篇文章中使用的是结构体计算intersect(小插曲:上篇使用的是结构体直接取成员变量,效率比本篇中调用结构体成员函数要低一些),与各索引算法中使用了较为复杂的类结构及派生关系比不能相提并论,如果将两次的表合起来对比,大约是内存索引所有节点为4.5s左右,使用rtree索引速度为3s左右(各个库的索引算法中的函数调用次数也不仅仅是本例中写的单一intersect函数),还是存在一定效率优势的


你可能感兴趣的:(ElfGIS,GIS开发)