c+中的矩阵生成与分解+;最佳实践? 假设我有一个C++函数,它有许多小函数,在每一个中,我通常需要一个矩阵浮点M1(n,p),其中n,p在运行时已知,包含中间计算的结果(不需要初始化M1,只是为了声明它,因为每个函数只覆盖M1的所有行)。

c+中的矩阵生成与分解+;最佳实践? 假设我有一个C++函数,它有许多小函数,在每一个中,我通常需要一个矩阵浮点M1(n,p),其中n,p在运行时已知,包含中间计算的结果(不需要初始化M1,只是为了声明它,因为每个函数只覆盖M1的所有行)。,c++,eigen,C++,Eigen,部分原因是,每个函数都在一个原始数据矩阵上工作,它不能修改,所以很多操作(排序、去义、球化)都需要在“别处”上完成 在每个函数中创建一个临时M1(n,p)是更好的做法,还是在main()中一次性地将其作为一个桶传递给每个函数,每个函数都可以将其用作废弃空间 氮和磷对氮和磷来说通常是中等大小的[10^2-10^4],而对磷来说是[5-100] (最初在codereview stackexchange上发布,但移到了这里) 最好,首先尝试在函数中定义矩阵。这绝对是更好的设计选择。但是如果您无法支持性

部分原因是,每个函数都在一个原始数据矩阵上工作,它不能修改,所以很多操作(排序、去义、球化)都需要在“别处”上完成

在每个函数中创建一个临时M1(n,p)是更好的做法,还是在main()中一次性地将其作为一个桶传递给每个函数,每个函数都可以将其用作废弃空间

氮和磷对氮和磷来说通常是中等大小的[10^2-10^4],而对磷来说是[5-100]

(最初在codereview stackexchange上发布,但移到了这里)


最好,首先尝试在函数中定义矩阵。这绝对是更好的设计选择。但是如果您无法支持性能损失,我认为“每个引用传递缓冲区”是可以的,只要您记住这些函数不再是线程安全的。如果在任何时候使用线程,每个线程都需要自己的缓冲区。

不要使用过早优化。创造一些工作正常、良好的东西,如果表现出速度缓慢,则在以后进行优化

(顺便说一句,我也不认为stackoverflow是正确的地方)

实际上,如果您想加快应用程序在大型矩阵上的运行速度,那么使用并发将是您的解决方案。如果您使用并发,那么如果您有一个大的全局矩阵,那么您可能会遇到更多的麻烦

从本质上说,这意味着一次不能进行多个计算,即使你有足够的内存

矩阵的设计需要优化。我们必须看看这个设计

因此,我通常会在你的代码中说,不,不要创建一个大的全局矩阵,因为它听起来与你想要用它做的事情不符

  • 堆分配确实非常昂贵
  • 过早优化是不好的,但是如果您的库非常通用并且矩阵非常庞大,那么寻求有效的设计可能还为时过早。毕竟,在积累了许多依赖项之后,您不想修改设计
  • 你可以在不同的层次上解决这个问题。例如,您可以通过在内存分配器级别(例如,每个线程内存池)处理堆分配开销来避免堆分配开销
  • 虽然堆分配很昂贵,但创建一个巨大的矩阵只是为了对矩阵执行一些相当昂贵的操作(通常是线性复杂度或更糟)。相对而言,在空闲存储区上分配矩阵与随后不可避免地要处理矩阵相比可能并不昂贵,因此与排序等函数的总体逻辑相比,它实际上可能相当便宜
  • 我建议您自然地编写代码,并将#3作为未来的可能性考虑在内。也就是说,不要在中间计算中引用矩阵缓冲区来加速临时表的创建。创建临时值并按值返回它们。正确性和良好、清晰的接口是第一位的

    这里的主要目标是分离矩阵的创建策略(通过分配器或其他方式),这为您提供了喘息空间,可以在不改变太多现有代码的情况下进行优化。如果您可以只修改所涉及函数的实现细节,或者更好的是,只修改矩阵类的实现,那么您就真正富裕了,因为这样您就可以在不改变设计的情况下自由地进行优化,从效率的角度来看,任何允许这样做的设计通常都是完整的


    警告:以下内容仅适用于您确实希望从每个周期中获得最大收益的情况。了解#4并为自己配备一名优秀的档案员是至关重要的。还值得注意的是,通过优化这些矩阵算法的内存访问模式,您可能会比尝试优化堆分配做得更好


    如果需要优化内存分配,请考虑使用像每个线程内存池那样的通用优化。例如,您可以让您的矩阵接受一个可选的分配器,但我在这里强调可选的,并且我还将首先通过一个简单的分配器实现强调正确性

    换言之:

    在每个函数中声明M1(n,p)更好吗 而是在main()中一次性地将其作为 每个函数都可以用作废料空间的一种桶

    继续,在每个函数中创建M1作为临时变量。尽量避免要求客户制作一些对他/她没有意义的矩阵,而只是为了计算中间结果。这将暴露一个优化细节,这是我们在设计接口时不应该做的事情(隐藏客户端不应该知道的所有细节)

    相反,如果您绝对希望该选项加速这些临时变量的创建(如可选的分配器),请关注更一般的概念。这适用于实际设计,如
    std::set

    std::set<int, std::less<int>, MyFastAllocator<int>> s; // <-- okay
    

    std::set s;// 在性能方面,需要外部提供的缓冲区具有优势,特别是当需要链接使用它的函数时

    然而,从用户的角度来看,它很快就会变得烦人

    <>我经常发现,C++中的简单之处是:简单地提供两种方式:
    int compute(Matrix const& argument, Matrix& buffer);
    
    inline int compute(Matrix const& argument) {
      Matrix buffer(argument.width, argument.height);
      return compute(argument, buffer);
    }
    
    这种非常简单的包装意味着代码只编写一次,并提供两个稍有不同的接口

    更复杂的api(采用
    int compute(Matrix const& argument, Matrix& buffer);
    
    inline int compute(Matrix const& argument) {
      Matrix buffer(argument.width, argument.height);
      return compute(argument, buffer);
    }