C++ 模板复杂图像读取类执行时间慢,声明和实现分离

C++ Slow execution time of template complex image reading class, with separated declaration and implementation

本文关键字:声明 分离 实现 执行时间 复杂 图像 读取 C++      更新时间:2023-10-16

我正在尝试制作读取复杂图像的二进制文件。我使用VisualStudio 2010。这是代码1

      #include <stdio.h>
      #include <conio.h>
      #include <iostream>
      #include <fstream>
      #include <math.h>
      #include <stdlib.h>
      #define NX 21843
      #define NY 22380
      #include <windows.h>
    class CTimer
    {
    public:
        CTimer() 
        {
            QueryPerformanceFrequency(&mqFreq);
        }
        ~CTimer() {}
        void Start() 
        {
            QueryPerformanceCounter(&mqStart);
        }
        void End() 
        {
            QueryPerformanceCounter(&mqEnd);
        }
        double GetTimeInSeconds()
        {
            return (mqEnd.QuadPart - mqStart.QuadPart)/static_cast<double>(mqFreq.QuadPart);
        }
    private:
        LARGE_INTEGER mqStart;
        LARGE_INTEGER mqEnd;
        LARGE_INTEGER mqFreq;
    };
    using namespace std;
int main(void)
{
    CTimer Time;
    Time.Start();
    ifstream INFILE;
    INFILE.open("e:\WORK\CIMG.bindat", ios::binary);
    short ***im_ptr;
    im_ptr = new short** [2];
    for (int i=0; i<2; i++){
        im_ptr[i] = new short* [NX];
        for (int j=0; j<NX; j++){
            im_ptr[i][j] = new short [NY];
        }
    }
    for(int i=0; i<2; i++){
        for (int j=0; j<NX; j++){
            for (int k=0; k<NY; k++){
                int index = i*NY*NX + j*NY + k;
                INFILE.read((char*)&im_ptr[i][j][k], sizeof(short));
                if (index == 2*NX*NY-1){
                    INFILE.close();}
            }
        }
    } 
    for (int i=0; i<2; i++){
        for (int j=0; j<NX; j++){
            delete[] im_ptr[i][j];
        }
        delete[] im_ptr[i];
    }
    delete[] im_ptr;
    Time.End();
    std::cout << Time.GetTimeInSeconds() << " seconds" << std::endl;
    std::cout << "nPress any key to exit" << std::endl;
    getch();
    return 0;
}

此代码工作正常,但仅适用于 SHORT 类型的图像。

我决定做模板类,它允许读取许多不同数据类型的图像,并将声明和实现分开。这是代码2

stdafx.h 的 - 声明我的班级

#pragma once
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
template<typename DataType> 
class SARDataLoader
{
private:
    int day,month,year; 
public:
    DataType*** LoadComplexImage(int, int);
    int DeleteImageFromMemory(DataType***, int);
}; 

SARDataLoader_C.cpp - 实现我的类

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <stdlib.h>
using namespace std;
template<typename DataType> 
DataType*** SARDataLoader<DataType>::LoadComplexImage(int NX, int NY){
    using namespace std;
    ifstream INFILE;
    INFILE.open(""e:\WORK\CIMG.bindat"", ios::binary);
    DataType ***im_ptr;
    im_ptr = new DataType** [2];
    for (int i=0; i<2; i++){
        im_ptr[i] = new DataType* [NX];
        for (int j=0; j<NX; j++){
            im_ptr[i][j] = new DataType [NY];
        }
    }
    for(int i=0; i<2; i++){
        for (int j=0; j<NX; j++){
            for (int k=0; k<NY; k++){
                int index = i*NY*NX + j*NY + k;
                INFILE.read((char*)&im_ptr[i][j][k], sizeof(DataType));
                if (index == 2*NX*NY-1){
                    INFILE.close();}
            }
        }
    }
    return im_ptr;
};
template<typename DataType> int SARDataLoader<DataType>::DeleteImageFromMemory(DataType*** im_ptr, int NX){
    for (int i=0; i<2; i++){
        for (int j=0; j<NX; j++){
            delete[] im_ptr[i][j];
        }
        delete[] im_ptr[i];
    }
    delete[] im_ptr;
    return 112345;
};
template class SARDataLoader<short>;//explicit instantiation short

主.cpp - 使用我的类

#include "stdafx.h"
#include <stdio.h>
#include <conio.h>
#include <iostream>
#include <fstream>
#include <math.h>
#define NX 21843
#define NY 22380
#include <windows.h>
class CTimer
{
public:
    CTimer() 
    {
        QueryPerformanceFrequency(&mqFreq);
    }
    ~CTimer() {}
    void Start() 
    {
        QueryPerformanceCounter(&mqStart);
    }
    void End() 
    {
        QueryPerformanceCounter(&mqEnd);
    }
    double GetTimeInSeconds()
    {
        return (mqEnd.QuadPart - mqStart.QuadPart)/static_cast<double>(mqFreq.QuadPart);
    }
private:
    LARGE_INTEGER mqStart;
    LARGE_INTEGER mqEnd;
    LARGE_INTEGER mqFreq;
};
using namespace std;
int main(void)
{
    CTimer Time;
    Time.Start();
    std::cout <<"nIMAGE1 Loading..."<< std::endl;
    SARDataLoader<short> IMG_LOADER;
    short ***im_ptr = IMG_LOADER.LoadComplexImage(NX, NY);
    int CODE = IMG_LOADER.DeleteImageFromMemory(im_ptr, NX);
    Time.End();
    double run_time = Time.GetTimeInSeconds();
    std::cout <<CODE<<"nRUNTIME "<<run_time<<" seconds"<<"n...Press any key to exit...n"<<std::endl;
    getch();
    return 0;
}

此代码有效,并且执行与 code1 相同的操作。我使用 CTimer 类进行代码 1 和代码 2 执行时间测量。它说 code2 的执行时间几乎是 code1 的三倍。谁能告诉我是什么原因造成的以及如何解决这个问题?

查看两个示例(非优化编译)生成的汇编代码,我注意到只有两个差异:

  1. 处理NXNY值需要在模板化版本中进行实际计算,因为它们作为参数传递,而原始代码是硬编码常量(通过 #define 秒)。然而,添加到模板化代码中的工作量看起来微不足道 - 我无法想象它们会导致执行时间的可衡量差异。

  2. 模板化代码在从LoadComplexImage()返回时调用ifstream INFILE的析构函数 - 此操作的执行时间包含在模板化版本的计时器中。 由于 INFILE 的 dtor 在原始代码中返回之前不会调用main()因此它不包含在该示例的计时中。

我可以相信ifstream的 dtor 可能需要可测量的时间(调试库中的 dtor 可能很慢)。 由于我不知道这些运行的时间是什么,我不知道它是否会增加 200%。但在我看来,如果不是全部的话,它可能是至少很大一部分增长的罪魁祸首。

要比较同类产品,您需要重新调整原始测试以包括INFILE的销毁,或者在这两种情况下将输入文件的打开移动到定时代码之外,并将其作为参考传递到模板化版本中(或使用全局进行测试)。 将输入文件的打开和关闭保持在定时区域之外似乎是对此内容进行基准测试的更好选择。