C++ 为什么在这样的递归代码中避免内存分配/释放并不能节省运行时间?
假设有一个函数C++ 为什么在这样的递归代码中避免内存分配/释放并不能节省运行时间?,c++,performance,profiling,allocation,C++,Performance,Profiling,Allocation,假设有一个函数foo\u,带有\u mem\u allocs,该函数通过对整数向量的递归操作,因此它需要在每个递归步骤中分配/删除缓冲区数组(类似于合并排序算法): 您假设动态分配一定很昂贵。你需要重新审视这种预先判断 像这样的微基准不会揭示内存分配的成本,因为用例是非常优化的。您总是分配相同大小的单个缓冲区,并在发出另一个分配请求之前立即释放它。任何合适的内存分配库每次都会给您相同的内存,从最近发布的所需大小的分配缓存中获取。代码路径非常短 但即使在现实的基准测试中,您也会发现一代又一代非常有
foo\u,带有\u mem\u allocs
,该函数通过对整数向量的递归操作,因此它需要在每个递归步骤中分配/删除缓冲区数组(类似于合并排序算法):
您假设动态分配一定很昂贵。你需要重新审视这种预先判断 像这样的微基准不会揭示内存分配的成本,因为用例是非常优化的。您总是分配相同大小的单个缓冲区,并在发出另一个分配请求之前立即释放它。任何合适的内存分配库每次都会给您相同的内存,从最近发布的所需大小的分配缓存中获取。代码路径非常短 但即使在现实的基准测试中,您也会发现一代又一代非常有才华的程序员为了您的利益而努力优化内存分配的结果,这样您就可以使用动态分配,而不必浪费时间进行过早的优化
作为一个小附言,不启用优化的微基准标记可能会产生其他不一致之处。例如,
std::vector
中未优化的元素访问可能比简单的int
s数组中的元素访问慢得多。这种差异可能足以完全掩盖一些动态分配的微小成本。您假设动态分配一定很昂贵。你需要重新审视这种预先判断
像这样的微基准不会揭示内存分配的成本,因为用例是非常优化的。您总是分配相同大小的单个缓冲区,并在发出另一个分配请求之前立即释放它。任何合适的内存分配库每次都会给您相同的内存,从最近发布的所需大小的分配缓存中获取。代码路径非常短
但即使在现实的基准测试中,您也会发现一代又一代非常有才华的程序员为了您的利益而努力优化内存分配的结果,这样您就可以使用动态分配,而不必浪费时间进行过早的优化
作为一个小附言,不启用优化的微基准标记可能会产生其他不一致之处。例如,
std::vector
中未优化的元素访问可能比简单的int
s数组中的元素访问慢得多。这种差异可能足以完全掩盖一些动态分配的微小成本。你是说“我尝试了这个,但我得到了一个令人困惑的时间结果”
这意味着你不知道是什么原因造成了这段时间。
如果你不知道是什么原因造成了这段时间,你所做的就是猜测。
即使是一个好的猜测也是一种猜测,而猜测的本质是可以欺骗人的
无需任何费用,非常简单,并准确地告诉您什么需要时间。没有猜测。你是说“我尝试了这个,但我得到了一个令人困惑的时间结果”
这意味着你不知道是什么原因造成了这段时间。
如果你不知道是什么原因造成了这段时间,你所做的就是猜测。
即使是一个好的猜测也是一种猜测,而猜测的本质是可以欺骗人的
无需任何费用,非常简单,并准确地告诉您什么需要时间。没有猜测。我测试了在Windows 10 64位下使用Visual Studio 2015进行编译,以及在Unix下使用G++进行编译您没有发布编译代码时使用的优化设置。如果您正在对未优化(即调试模式)的代码计时,那么您的结果将毫无意义。Visual Studio版本尤其如此,迭代器检查在调试模式下完成。@PaulMcKenzie感谢您的评论。不,我不是在调试模式下编译。我正在64位版本模式下编译。在Unix中,我只是使用
g++-o test.cpp
编译。我编辑了问题以添加这些信息。您需要指定一个优化标志,即-O2
或-O3
@user-如果未进行优化,您会告诉编译器“请不要使其快速运行”。那么,它的运行速度有多慢,就相当无趣了。这就像让乌塞恩·博尔特慢慢走100米。@user2019840:除了我在回答中所说的,你的递归是指数的;当sz
为1000万时,它会递归几十次。由于内存分配的成本与分配的大小无关(除非所分配的内存正在被清除,在这种情况下它不是),相对于1000万次迭代循环,24次分配几乎总是产生微不足道的影响。我测试了在Windows 10 64位下使用Visual Studio 2015进行编译,以及在Unix下使用G++进行编译您没有发布编译代码时使用的优化设置。如果您正在对未优化(即调试模式)的代码计时,那么您的结果将毫无意义。Visual Studio版本尤其如此,迭代器检查在调试模式下完成。@PaulMcKenzie感谢您的评论。不,我不是在调试模式下编译。我正在64位版本模式下编译。在Unix中,我只是使用g++-o test.cpp
编译。我编辑了问题以添加这些信息。您需要指定一个优化标志,即-O2
或-O3
@user-如果未进行优化,您会告诉编译器“请不要使其快速运行”。那么,它的运行速度有多慢,就相当无趣了。这就像让乌塞恩·博尔特慢慢走100米。@user2019840:除了我在回答中所说的,你的递归是指数的;当sz
为1000万时,它会递归几十次。由于内存分配的成本不相关
void foo_with_mem_allocs(vector<int>& v, int c)
{
if (c > 1)
{
c /= 2;
foo_with_mem_allocs(v, c);
int *buffer = new int[v.size()];
for (int i = 0; i < v.size(); i++)
{
buffer[i] = v[i];
}
for (int j = 0; j < c; j++)
v[j] = buffer[j] * c;
delete[] buffer;
}
}
void foo_with_buffer(vector<int>& v, int c, vector<int>& buffer)
{
if (c > 1)
{
c /= 2;
foo_with_buffer(v, c, buffer);
for (int i = 0; i < v.size(); i++)
{
buffer[i] = v[i];
}
for (int j = 0; j < c; j++)
v[j] = buffer[j] * c;
}
}
//////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;
void foo_with_mem_allocs(vector<int>& v, int c) {
if (c > 1)
{
c /= 2;
foo_with_mem_allocs(v, c);
int *buffer = new int[v.size()];
for (int i = 0; i < v.size(); i++)
{
buffer[i] = v[i];
}
for (int j = 0; j < c; j++)
v[j] = buffer[j] * c;
delete[] buffer;
}
}
void foo_with_buffer(vector<int>& v, int c, vector<int>& buffer) {
if (c > 1)
{
c /= 2;
foo_with_buffer(v, c, buffer);
for (int i = 0; i < v.size(); i++)
{
buffer[i] = v[i];
}
for (int j = 0; j < c; j++)
v[j] = buffer[j] * c;
}
}
int main(int argc, char** argv) {
// setting a random seed using clock
srand(time(NULL));
// print out header of output information
cout << "Size\tMethod\t\t\tTicks\tSecs" << endl;
// iterate over possible input data sizes specified in the arguments.
int sz = 100000;
vector<int> v(sz);
vector<int> v1(sz);
vector<int> v2(sz);
vector<int> buffer(sz);
for (int i = 0; i < sz; ++i)
v[i] = v1[i] = v2[i] = rand();
// timing foo that does a bunch of memory allocations/deallocations
clock_t t = clock();
foo_with_mem_allocs(v1, sz);
t = clock() - t;
cout << sz << "\tWith mem alloc\t\t" << t << "\t"
<< (double)t / CLOCKS_PER_SEC << endl;
// timing foo that uses buffers to avoid memory allocations/deallocations
t = clock();
foo_with_buffer(v2, sz, buffer);
t = clock() - t;
cout << sz << "\tWith buffer\t\t" << t <<
"\t" << (double)t / CLOCKS_PER_SEC << endl;
bool changed = false;
for (int i = 0; i < v1.size(); i++)
if (v1[i] != v2[i])
changed = true;
if (changed)
std::cout << "Note: results from the two functions were different.\n" << std::endl;
else
std::cout << "Note: results from the two functions were identical.\n" << std::endl;
return 0;
}
///////////////////////////////////////////////////////////////////////////////