如何组织时间密集型代码以避免开销

时间:2017-06-11 18:14:23

标签: c++ arrays memory optimization memory-management

我正在努力教自己如何编写更快的代码(代码使用更少的指令)。我想创建一个人工神经网络(ANN)。如果您对人工神经网络一无所知,您可能仍然可以帮助我,因为我的问题更多的是编写比人工智能更快的代码。基本上,我将有一大堆双打,我需要进行大量的数学运算。我可以像这样分配我的数组:

class Network {
    double *weights;
    double *outputs;

    public:
    Network()
    {

    }
    Network(int * sizes, int numofLayers)
    {
        int sum = 0;
        int neuron_count = 0;
        // this just ensures my weight array is the right size
        for(int i = 0; i < numofLayers-1; i++)
        {
            neuron_count += sizes[i];
            sum = sum + sizes[i]*sizes[i+1];
        }
        neuron_count += sizes[numofLayers-1];
        weights = new double[sum];
        outputs = new double[neuron_count];
    }
    ~Network()
    {
        delete[] weights;
        delete[] outputs;
    }
};

但是,我不喜欢这个,因为我使用“new”,我知道我可能会在以后开启自己的一堆内存管理问题。我知道堆栈分配更快,如果我能根据这段摘录提供帮助,我不应该使用动态内存:

  

“动态内存分配是使用operator new和delete完成的   或者使用malloc和free函数。这些运算符和函数   消耗大量的时间。内存的一部分称为堆   保留用于动态分配。堆很容易变成   分配不同大小的对象时碎片化   按随机顺序解除分配。堆管理器可以花费大量时间   清理不再使用的空间并寻找空置   空间。这称为垃圾收集。分配的对象   顺序不一定按顺序存储在存储器中。他们   当堆变成时,可能会分散在不同的地方   支离破碎。这使数据缓存效率低下“

     

使用C ++优化软件适用于Windows,Linux的优化指南   和Mac平台作者:Agner Fog。

但是,数组权重和输出将被我想在类Network中创建的许多函数使用;如果他们是本地的并且被解除分配,那将无效。无论是使用new关键字还是仅为整个神经网络制作一个巨大的函数,我都感到困惑。

在正常情况下,我会说可读性对于维护代码更重要,但我并不担心,因为这更多是要学习编写快速代码。如果为时间繁重的算法编写代码的人为了使事情变得最快而编写大函数,那就没问题了。我只想知道。

另一方面,如果我的数组只被分配一次并将在整个程序中使用,那么使用堆分配是否更聪明,因为有问题的开销应该只发生一次?然后专注于使用内在函数或数学繁重代码中的某些东西。是否有任何其他缺点,例如,如果我访问堆很多,以便将内存移动到缓存,这比访问堆栈上的内存更加密集,以便堆栈移动到缓存(即使堆应该保持不变)?编写速度非常快的代码的人绝对会避免新的代码吗?如果是这样,我有什么选择以这种方式组织我的程序,并仍然保持我的数组是由用户指定的任意大小?

1 个答案:

答案 0 :(得分:0)

特别是堆内存没有任何坏或慢。但是,在某些情况下,您应该谨慎使用它。真正伤害性能的主要问题之一是当堆内存中散布有大量小对象时(因为每个分配几乎可以在内存中的任何位置放置新对象)。在这种情况下会发生这种情况,例如:

for(int i = 0; i < some_size; ++i)
  some_array = new MySmallObject;

这里的问题是当你长时间使用some_array进行许多操作时,缓存命中率可能会非常低(这是你在与现代硬件竞争时可以使用的主要参数之一性能)最终导致性能下降。这是因为对存储器some_array[i]的每次下一次访问都可能需要访问分散的存储器位置,而不能适应缓存预测算法。

另一个问题是当您长时间不使用已分配的内存并且在应用程序中经常长时间重新分配内存时。这也会影响你的性能,因为动态内存分配操作不是一件容易的事情,它源于动态内存的本质。此内存不知道您要分配的数据以及何时分配它们。所以,简单地说,它是一个记忆片段,知道哪些不忙。现在成像你经常分配很多小物件,擦除一些旧物件。那失控了。根据动态内存的实现,可能会出现内存碎片等操作,这种情况可能取决于您分配/释放内存的频率。当然,这也会影响性能。

这是与两个动态内存分配相关的两个性能杀手。一个合法的问题是如何解决这个问题?通过重新设计程序或/和重新设计算法,可能会解决许多问题。但是,有些情况下无法做到,那么该选项将是自定义分配器。分配器是提供分配/释放内存的操作的类型。这些分配器是模板参数和构造函数参数,例如,遍布std容器,如vectorstring等。另一个选项是重载全局或成员operator new。当你拥有自定义的东西时,当然可以使用你的自定义设计来分配你的对象如何分配内存,它可能只是一个单例内存池。

手动内存管理可能是一项非常复杂的任务。其中一个简单的解决方案可能就是使用malloc获取内存块,并将其用作堆栈类型,只需向下和向上移动指针即可。如果池内存不足,则会中止或使用OS动态内存。为什么我说malloc因为在堆栈上使用大内存预留是非常危险的,堆栈是非常有限的,你无法控制它,使用自定义分配器,它很容易超出堆栈大小在现代操作系统上该选项可能是一个静态内存,但它可能非常不灵活,你永远不能再扩展你的池了,malloc你可以有一个简单的大内存块列表并添加一个新内存块当前一个用尽时。

这一切都在记忆中,当然,还有更多要说的,但那是我想要启发的要点。其他性能改进在于填补所有处理器电源容量:加载所有内核,采用矢量化。

对不起,我没有直接回答你的问题。因为在处理这类问题时要考虑太多。这取决于你的情况:域名是什么,需求是什么,硬件类型,任务是什么等等。