在我编写的一些代码中,一个新的 FenwickTree 是由向量
ind
构造的,每个循环都保持相同的大小。 FenwickTree 的大小与输入向量相同。编译器是否会在循环开始时不重复优化构造对象并分配内存,然后在循环结束时销毁对象?我更熟悉 C,其中内存管理是显式的。
示例
#include <vector>
#include <iostream>
struct fenwick_tree
{
size_t len; // 0-based len
std::vector<int> t; // 1-based tree, indexes [1:len]
fenwick_tree(std::vector<int> const &a) :
len(a.size()), t(len + 1, 0)
{
for (size_t i = 0; i < len; ++i)
add_to(i, a[i]);
}
// 0-based input
int sum_to(size_t r) const
{
int s = 0;
for (++r; r > 0; r -= r & -r)
s += t[r];
return s;
}
// 0-based input
void add_to(size_t i, int delta)
{
for (++i; i <= len; i += i & -i)
t[i] += delta;
}
};
int main()
{
std::vector<int> ind(10, 10);
for (int i = 1; i < 10; ++i)
{
ind.assign(10, 10*i);
auto fen = fenwick_tree(ind);
std::cout << fen.sum_to(9) << std::endl;
}
}
这是ltrace的结果。我可以看到memset和flush,但我不知道是否
_ZNSt8ios_base4InitC1Ev(0x58659f9b8151, 0x7ffd6d84bd48, 0x7ffd6d84bd58, 0x58659f9b7d30) = 0x72f4c90224b8
__cxa_atexit(0x72f4c92bf840, 0x58659f9b8151, 0x58659f9b8008, 0x72f4c942ada0) = 0
_Znwm(40, 0x7ffd6d84bd48, 0x7ffd6d84bd58, 0x58659f9b7d38) = 0x5865aab3ceb0
_Znwm(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
_ZNSolsEi(0x58659f9b8040, 100, 2, 10) = 0x58659f9b8040
_ZNSo3putEc(0x58659f9b8040, 10, 0x72f4c9423370, 0x7ffd6d84bac8100
) = 0x58659f9b8040
_ZNSo5flushEv(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
_ZdlPvm(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
_Znwm(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
_ZNSolsEi(0x58659f9b8040, 200, 2, 20) = 0x58659f9b8040
_ZNSo3putEc(0x58659f9b8040, 10, 0x72f4c9423370, 3122200
) = 0x58659f9b8040
_ZNSo5flushEv(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
_ZdlPvm(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
_Znwm(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
_ZNSolsEi(0x58659f9b8040, 300, 2, 30) = 0x58659f9b8040
_ZNSo3putEc(0x58659f9b8040, 10, 0x72f4c9423370, 3123300
) = 0x58659f9b8040
_ZNSo5flushEv(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
_ZdlPvm(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
_Znwm(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
_ZNSolsEi(0x58659f9b8040, 400, 2, 40) = 0x58659f9b8040
_ZNSo3putEc(0x58659f9b8040, 10, 0x72f4c9423370, 3124400
) = 0x58659f9b8040
_ZNSo5flushEv(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
_ZdlPvm(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
_Znwm(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
_ZNSolsEi(0x58659f9b8040, 500, 2, 50) = 0x58659f9b8040
_ZNSo3putEc(0x58659f9b8040, 10, 0x72f4c9423370, 3125500
) = 0x58659f9b8040
_ZNSo5flushEv(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
_ZdlPvm(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
_Znwm(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
_ZNSolsEi(0x58659f9b8040, 600, 2, 60) = 0x58659f9b8040
_ZNSo3putEc(0x58659f9b8040, 10, 0x72f4c9423370, 3126600
) = 0x58659f9b8040
_ZNSo5flushEv(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
_ZdlPvm(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
_Znwm(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
_ZNSolsEi(0x58659f9b8040, 700, 2, 70) = 0x58659f9b8040
_ZNSo3putEc(0x58659f9b8040, 10, 0x72f4c9423370, 3127700
) = 0x58659f9b8040
_ZNSo5flushEv(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
_ZdlPvm(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
_Znwm(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
_ZNSolsEi(0x58659f9b8040, 800, 2, 80) = 0x58659f9b8040
_ZNSo3putEc(0x58659f9b8040, 10, 0x72f4c9423370, 3128800
) = 0x58659f9b8040
_ZNSo5flushEv(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
_ZdlPvm(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
_Znwm(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
_ZNSolsEi(0x58659f9b8040, 900, 2, 90) = 0x58659f9b8040
_ZNSo3putEc(0x58659f9b8040, 10, 0x72f4c9423370, 3129900
) = 0x58659f9b8040
_ZNSo5flushEv(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
_ZdlPvm(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
_ZdlPvm(0x5865aab3ceb0, 40, 0x5865aab3c, 2) = 0
+++ exited (status 0) +++
来自demangler.com
std::ios_base::Init::Init()(0x58659f9b8151, 0x7ffd6d84bd48, 0x7ffd6d84bd58, 0x58659f9b7d30) = 0x72f4c90224b8
__cxa_atexit(0x72f4c92bf840, 0x58659f9b8151, 0x58659f9b8008, 0x72f4c942ada0) = 0
operator new(unsigned long)(40, 0x7ffd6d84bd48, 0x7ffd6d84bd58, 0x58659f9b7d38) = 0x5865aab3ceb0
operator new(unsigned long)(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
std::ostream::operator<<(int)(0x58659f9b8040, 100, 2, 10) = 0x58659f9b8040
std::ostream::put(char)(0x58659f9b8040, 10, 0x72f4c9423370, 0x7ffd6d84bac8100
) = 0x58659f9b8040
std::ostream::flush()(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
operator delete(void*, unsigned long)(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
operator new(unsigned long)(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
std::ostream::operator<<(int)(0x58659f9b8040, 200, 2, 20) = 0x58659f9b8040
std::ostream::put(char)(0x58659f9b8040, 10, 0x72f4c9423370, 3122200
) = 0x58659f9b8040
std::ostream::flush()(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
operator delete(void*, unsigned long)(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
operator new(unsigned long)(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
std::ostream::operator<<(int)(0x58659f9b8040, 300, 2, 30) = 0x58659f9b8040
std::ostream::put(char)(0x58659f9b8040, 10, 0x72f4c9423370, 3123300
) = 0x58659f9b8040
std::ostream::flush()(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
operator delete(void*, unsigned long)(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
operator new(unsigned long)(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
std::ostream::operator<<(int)(0x58659f9b8040, 400, 2, 40) = 0x58659f9b8040
std::ostream::put(char)(0x58659f9b8040, 10, 0x72f4c9423370, 3124400
) = 0x58659f9b8040
std::ostream::flush()(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
operator delete(void*, unsigned long)(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
operator new(unsigned long)(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
std::ostream::operator<<(int)(0x58659f9b8040, 500, 2, 50) = 0x58659f9b8040
std::ostream::put(char)(0x58659f9b8040, 10, 0x72f4c9423370, 3125500
) = 0x58659f9b8040
std::ostream::flush()(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
operator delete(void*, unsigned long)(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
operator new(unsigned long)(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
std::ostream::operator<<(int)(0x58659f9b8040, 600, 2, 60) = 0x58659f9b8040
std::ostream::put(char)(0x58659f9b8040, 10, 0x72f4c9423370, 3126600
) = 0x58659f9b8040
std::ostream::flush()(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
operator delete(void*, unsigned long)(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
operator new(unsigned long)(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
std::ostream::operator<<(int)(0x58659f9b8040, 700, 2, 70) = 0x58659f9b8040
std::ostream::put(char)(0x58659f9b8040, 10, 0x72f4c9423370, 3127700
) = 0x58659f9b8040
std::ostream::flush()(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
operator delete(void*, unsigned long)(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
operator new(unsigned long)(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
std::ostream::operator<<(int)(0x58659f9b8040, 800, 2, 80) = 0x58659f9b8040
std::ostream::put(char)(0x58659f9b8040, 10, 0x72f4c9423370, 3128800
) = 0x58659f9b8040
std::ostream::flush()(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
operator delete(void*, unsigned long)(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
operator new(unsigned long)(44, 11, 0x5865aab3ced8, 0x5865aab3ced8) = 0x5865aab3cee0
memset(0x5865aab3cee0, '\0', 44) = 0x5865aab3cee0
std::ostream::operator<<(int)(0x58659f9b8040, 900, 2, 90) = 0x58659f9b8040
std::ostream::put(char)(0x58659f9b8040, 10, 0x72f4c9423370, 3129900
) = 0x58659f9b8040
std::ostream::flush()(0x58659f9b8040, 0x5865aab3cf20, 0x72f4c9423370, 0x72f4c8f14887) = 0x58659f9b8040
operator delete(void*, unsigned long)(0x5865aab3cee0, 44, 0x72f4c9423370, 3072) = 0
operator delete(void*, unsigned long)(0x5865aab3ceb0, 40, 0x5865aab3c, 2) = 0
exited (status 0)
看起来好像调用了一堆new和delete?
编译器将优化
sizeof(fen)
的堆栈分配。这是非常微不足道的,以至于一些编译器甚至在调试模式下也会这样做;这甚至不是他们内部设计的优化步骤。
编译器不会优化
fenwick_tree::t
的分配。向量的长度可以在运行时变化,并且它不是 sizeof(fenwick_tree)
的一部分。然而,标准库实现通常会回收内存。这种回收可以发生在内部operator new
,因此痕迹仍然可见。