CFD中文网

    CFD中文网

    • 登录
    • 搜索
    • 最新

    可以讨论一个编程问题吗?这样写C++代码效率为何这么低?

    Algorithm
    5
    19
    5003
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • 悬
      悬铃神木 最后由 编辑

      我在写一个CFD程序的时候碰到的问题,我想使用c++的特性把double数组封装成一个新的类,这样就可以重载运算符,像使用matlab那样直接进行矩阵层次的操作。
      比如,在c++里要分别对元素操作:

      for(int i=0;i<10000;i++)C[i]=A[i]+B[i]
      

      用matlab就可以直接写:

      C=A+B;
      

      但我写成一个类后,运算效率却比直接这样运行慢太多,甚至比matlab慢很多(C++应该比matlab快才对吧?)
      比如如下代码:

      #include "pch.h"
      #include <iostream>
      #include <time.h>  
      
      class Array
      {
      public:
      	Array(int S):Size(S)                          //构造函数
      	{
      		p = new double[Size];
      	}
      
      	Array(int S, double n):Size(S)        //构造函数
      	{
      		p = new double[Size];
      		for (int i = 0; i < Size; i++)p[i] = n;
      	}
      
      	~Array()                       //析构函数
      	{
      		delete[] p;
      	}
      
      	Array & operator=(const Array & A)                      //重载=
      	{
      		if (this == &A)
      			return *this;
      		delete[] p;
      		Size = A.Size;
      		p = new double[Size];
      		memcpy(p, A.p, Size * sizeof(double));
      		return *this;
      	}
      
      	Array operator*(const Array & A)                 //对应元素*
      	{
      		Array result(Size);
      		for (int i = 0; i < Size; i++)result.p[i] = p[i] * A.p[i];
      		return result;
      	}
      
      private:
      	int Size;
      	double *p;
      };
      
      
      int main()
      {
      	clock_t time1, time2;
      	Array A(10000, 1.0);
      	Array B(10000, 1.0);
      	Array C(10000, 1.0);
      	time1 = clock();
      	for (int t = 0; t < 1000000; t++)C = A * B;
      	time2 = clock();
      	std::cout<< "Time1 = " << (double)(time2 - time1) / CLOCKS_PER_SEC << "S" << std::endl;
      //测试直接使用double数组的速度
      	double a[10000];
      	double b[10000];
      	double c[10000];
      	for (int i = 0; i < 10000; i++)a[i] = 1, b[i] = 1;
      
      	time1 = clock();
      	for (int t = 0; t < 1000000; t++) 
      		for (int i = 0; i < 10000; i++)c[i]=a[i] * b[i];
      	time2 = clock();
      	std::cout << "Time2 = " << (double)(time2 - time1) / CLOCKS_PER_SEC << "S" << std::endl;
      
      	system("pause");
      	return 0;
      }
      
      

      输出:
      Time1 = 10.4S
      Time2 = 0.492S
      使用对象的时候慢了20多倍

      用matlab做相同工作的时候:

      A=ones(10000,1);
      B=zeros(10000,1);
      t1=clock;
      for i=1:1000000
          C=A.*B;
      end
      t2=clock;
      T=etime(t2,t1)
      

      输出:
      T =4.8610
      我这样写是否犯了什么错误?:qichuang: 这种情况下应该如何提升c++的效率呢?

      1 条回复 最后回复 回复 引用
      • 李东岳
        李东岳 管理员 最后由 编辑

        我觉得问题在于你这里面有循环嵌套:

        for (int t = 0; t < 1000000; t++) 
        		for (int i = 0; i < 10000; i++) c[i]=a[i] * b[i];
        

        线上CFD课程开始报名:http://www.dyfluid.com/class.html

        CFD高性能服务器 http://dyfluid.com/servers.html

        悬 1 条回复 最后回复 回复 引用
        • 悬
          悬铃神木 @李东岳 最后由 编辑

          这个循环嵌套直接用double数组速度很快啊,用了0.492S
          上面那个用了我写的类的地方:

          for (int t = 0; t < 1000000; t++)C = A * B;
          

          这个地方很慢,做相同的事情用了 10.4S

          1 条回复 最后回复 回复 引用
          • 李东岳
            李东岳 管理员 最后由 编辑

            可能是在你的*号里面多次调用构造函数所致,你这是调用了100000次构造函数:huahua:

            线上CFD课程开始报名:http://www.dyfluid.com/class.html

            CFD高性能服务器 http://dyfluid.com/servers.html

            悬 1 条回复 最后回复 回复 引用
            • 悬
              悬铃神木 @李东岳 最后由 编辑

              谢谢东岳老师,测试了一下确实是这样,构造和return这个类耗费时间很多……
              但是没想到好方法……要实现相关功能+-*/必须返回一个相同的类……
              不知道成熟的代码是怎么解决这个问题的:xinlei:

              1 条回复 最后回复 回复 引用
              • 浪
                浪迹天大 最后由 编辑

                你需要什么样的操作呢?自带的各种类都无法满足吗?

                OpenFOAM 学习交流:https://openfoam.top

                悬 1 条回复 最后回复 回复 引用
                • 悬
                  悬铃神木 @浪迹天大 最后由 编辑

                  希望像matlab那样可以直接在矩阵层面操作,这样写出程序来简洁一些,我不知道有哪种自带的类可以满足,我只试过vector,但是并不比我自己写的类快。

                  1 条回复 最后回复 回复 引用
                  • 李东岳
                    李东岳 管理员 最后由 编辑

                    Array operator*(const Array& A) const
                    {
                        for (int i = 0; i < Size; i++)
                        {
                            p[i] += B.p[i];
                        }
                        return *this;
                    }
                    

                    这样会不会快一些?你试试

                    线上CFD课程开始报名:http://www.dyfluid.com/class.html

                    CFD高性能服务器 http://dyfluid.com/servers.html

                    悬 1 条回复 最后回复 回复 引用
                    • W
                      wwzhao 教授 最后由 编辑

                      1. 用类封装和用数组的运行效率不会差那么多。你这个程序的第二个循环可能被vs的cl.exe编译器优化过了,所以运行时间这么短。

                      2. MATLAB对矩阵的操作都底层调用的是Fortran或C语言写的库,这些库都是经过高度优化过的,执行效率非常高。

                      悬 1 条回复 最后回复 回复 引用
                      • 悬
                        悬铃神木 @李东岳 最后由 编辑

                        7.415S,确实快了一些。但是这样写相当于改变了两个被乘数组之一,这样限制了它的使用方式,像C=A+A*B这样的或者更复杂的表达式就很难写了。我试了一下,确实如楼下所言是优化的问题,零优化情况下就和直接用数组的方式没有这么大的差距。

                        1 条回复 最后回复 回复 引用
                        • 悬
                          悬铃神木 @wwzhao 最后由 李东岳 编辑

                          谢谢您的回答!试了一下确实是编译器优化带来的问题,我在linux系统下使用g++编译这串代码,以下分别是优化等级-O0(无优化)、-O1、-O2、-O3时得到的运行速度:

                          -O0
                          Time1 = 47.759S
                          Time2 = 29.2934S
                          -O1
                          Time1 = 16.7366S
                          Time2 = 4.35349S
                          -O2
                          Time1 = 11.9593S
                          Time2 = 1e-06S
                          -O3:
                          Time1 = 12.2002S
                          Time2 = 0S
                          

                          可见无优化时虽然我这种使用类的写法也要慢一些,但两者在一个数量级上,优化等级越高,直接使用数组的方法优势就越大了……
                          编译器的优化好神奇哦!:duang:
                          看来以后还是尽量去用别人写的成熟的函数库吧…… :huahua:

                          1 条回复 最后回复 回复 引用
                          • Z
                            Zephyr 最后由 编辑

                            你的new/delete是罪魁祸首,loop 1000000次意味着你new/delete了1000000次array C,尽量不要动态分配内存,除非你有不得不这样写的原因。如果你知道你要在代码里反复使用A, B, C三个矩阵,你应该一开始就分配好而不是在结构体里new。matlab里你可以随便写C = A + B 是因为matlab已经替你做了内存管理,你自己写代码的时候就要做到方方面面兼顾了。

                            悬 1 条回复 最后回复 回复 引用
                            • 悬
                              悬铃神木 @Zephyr 最后由 编辑

                              谢谢您的回答!我试了下不使用动态分配而用固定大小的数组p[10000]来代替,确实提高了一些效率,从10S+变成了7S+。
                              但我这里确实需要动态分配,因为我希望做成一个有一定通用性的矩阵类来储存各种流场信息,我无法在编程时就知道需要多大内存,也许这次计算只要几千网格,也许下次就需要几千万网格。

                              Z 1 条回复 最后回复 回复 引用
                              • Z
                                Zephyr @悬铃神木 最后由 编辑

                                @悬铃神木 thrust, boost这些库都挺不错的。。

                                悬 1 条回复 最后回复 回复 引用
                                • 悬
                                  悬铃神木 @Zephyr 最后由 编辑

                                  @Zephyr 嗯嗯,感谢,我去尝试一些库

                                  李东岳 1 条回复 最后回复 回复 引用
                                  • 李东岳
                                    李东岳 管理员 @悬铃神木 最后由 编辑

                                    @悬铃神木 你测试的结果是不是调用100000次构造函数和1次的区别不大?

                                    目前我在每个网格都构造一次构造函数,琢磨要不要把这个删掉

                                    线上CFD课程开始报名:http://www.dyfluid.com/class.html

                                    CFD高性能服务器 http://dyfluid.com/servers.html

                                    悬 1 条回复 最后回复 回复 引用
                                    • 悬
                                      悬铃神木 @李东岳 最后由 编辑

                                      是的,调用1000000次构造函数和1次,带来的差别不大

                                      1 条回复 最后回复 回复 引用
                                      • 李东岳
                                        李东岳 管理员 最后由 编辑

                                        感谢分享 楼主好人

                                        :xiexie: :xiexie: :xiexie: :xiexie:

                                        线上CFD课程开始报名:http://www.dyfluid.com/class.html

                                        CFD高性能服务器 http://dyfluid.com/servers.html

                                        悬 1 条回复 最后回复 回复 引用
                                        • 悬
                                          悬铃神木 @李东岳 最后由 编辑

                                          是我应该感谢cfd中文网的各位老师,对我的帮助很大.
                                          :xiexie: :xiexie: :xiexie: :xiexie: :xiexie: :xiexie: :xiexie: :xiexie:
                                          另外,我测试的这个构造函数和析构函数只承担了new和delete一个double[]的任务,如果是功能更复杂的构造函数,就不好说了。

                                          1 条回复 最后回复 回复 引用
                                          • First post
                                            Last post