我在 Android NDK 中遇到了非常奇怪的事情。
我有一个循环
#include <chrono>
#include <android/log.h>
#include <vector>
while (true)
{
const int sz = 2048*2048*3;
std::vector<unsigned char> v;
{
auto startTime = std::chrono::system_clock::now();
v.resize(sz);
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - startTime);
__android_log_print(ANDROID_LOG_ERROR, "READFILE 1", "v.resize(%d) time : %lld\n", sz, duration.count());
}
{
auto startTime = std::chrono::system_clock::now();
v.resize(0);
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - startTime);
__android_log_print(ANDROID_LOG_ERROR, "READFILE 2", "v.resize(0) time : %lld\n", duration.count());
}
{
auto startTime = std::chrono::system_clock::now();
v.resize(sz);
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - startTime);
__android_log_print(ANDROID_LOG_ERROR, "READFILE 3", "v.resize(%d) time : %lld\n", sz, duration.count());
}
}
我得到了一个日志:
34.4171: v.resize(12582912) time : 845977
34.9682: v.resize(0) time : 550995
35.5293: v.resize(12582912) time : 561165
36.6121: v.resize(12582912) time : 530845
37.1612: v.resize(0) time : 548528
37.7183: v.resize(12582912) time : 556559
38.7811: v.resize(12582912) time : 515162
39.3312: v.resize(0) time : 550630
39.8883: v.resize(12582912) time : 556319
40.9711: v.resize(12582912) time : 530739
41.5182: v.resize(0) time : 546654
42.0733: v.resize(12582912) time : 554924
43.1321: v.resize(12582912) time : 511659
43.6802: v.resize(0) time : 547084
44.2373: v.resize(12582912) time : 557001
45.3201: v.resize(12582912) time : 530313
所以,首先
- 如您所见,仅
resize(0)
就花费了 550 毫秒...应该是 最多 1 微秒而非 MILLI - 其次,为什么
resize(size) 再次需要 550 毫秒
如果 vector 的容量没有改变?
这是两个非常奇怪的行为。
如果您不相信我,欢迎您自行查看这段代码:)但只需在 Android NDK 上查看,而不是 Visual Studio项目,因为它在那里可以正常工作。
看起来确实像bug...
或者我做错了什么?
编辑
我检查了如果转到 resize()
方法,我会进入这样的循环
template <class _Tp, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
void
__vector_base<_Tp, _Allocator>::__destruct_at_end(pointer __new_last) _NOEXCEPT
{
pointer __soon_to_be_end = __end_;
while (__new_last != __soon_to_be_end)
__alloc_traits::destroy(__alloc(), _VSTD::__to_raw_pointer(--__soon_to_be_end));
__end_ = __new_last;
}
因此,这意味着有一个循环遍历调整大小范围内的每个元素并调用 destroy
如果你持有的不是具有析构函数的平凡对象,那么没有问题,但是如果你持有 vector (就像我的例子) int 对象,这些对象是平凡的并且它们没有析构函数,所以......这是非常奇怪的行为,如何从实际上没有析构函数的对象调用析构函数?
看起来像是编译器错误吗?
最佳答案
首先,许多库功能的实现强烈依赖于编译器优化。删除容器中的对象可以调用 destroy ,而对于普通可破坏的对象则不会执行任何操作。如果它什么都不做,那么所有逻辑都将被编译器优化掉。 STL 中销毁对象涉及大量逻辑,just take a look 。本质上调用 destroy 是为了确保它处理所有情况,包括自定义分配器。它必须进行编译,因此对于普通类型,它必须解析为已定义的内容,并且不执行任何操作仍然是已定义的内容。只是让代码尽可能干净。单一职责,释放器决定如何以及是否需要销毁对象。
至于您的主要问题,您使用优化吗?这是第一个也是最重要的问题。任何未经优化的代码都只能保证可以工作。对于未优化的代码,即使引用提供的复杂性也可能不同。可以清楚地看到第一次重新分配花费了几乎两倍的时间,其余的都相当稳定。
您在进行此类其他操作时是否有更好的体验?您是否尝试过与普通数组性能进行比较?
关于Android NDK : vector. resize() 太慢,与分配有关?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58745415/