Android NDK:vector.resize()太慢,与分配有关吗?
我在Android NDK中遇到了一件非常奇怪的事情 我有一个循环Android NDK:vector.resize()太慢,与分配有关吗?,android,c++,android-ndk,Android,C++,Android Ndk,我在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
#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());
}
}
因此,首先
resize(0)
得到了550毫秒。。。应该是
最大1微秒,非毫秒resize(size)
如果载体的容量不变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;
}
模板
内联\u LIBCPP\u内联\u可见性
无效的
__向量基::uuu destruct_u在_u端(指针uu new_ulast)u无异常
{
指针uuu soon_uto_ube_end=uuu end;
while(\uuuu new\uu last!=\uuuuu soon\uu to\uu be\uu end)
__alloc_traits::destroy(u_alloc(),_VSTD::u to_raw_pointer(--u soon_to_be_end));
__结束=新的最后一个;
}
所以,这意味着在resize范围内的每个元素上都有一个循环,并调用destroy
如果你持有的不是有析构函数的平凡对象,但是如果你持有的是向量(就像我的例子)中的int对象是平凡的,它们没有析构函数,那么就没有问题了,所以。。。这是一种非常奇怪的行为,如何从实际上没有析构函数的对象调用析构函数
它看起来像编译器bug吗?首先,许多库功能的实现强烈依赖于编译器优化。删除容器中的对象可以调用destroy,而这反过来对于微不足道的可破坏对象将无能为力。若它什么也不做,那个么编译器将优化所有逻辑。在STL中销毁对象涉及很多逻辑。本质上,调用destroy是为了确保它处理所有情况,包括自定义分配器。它必须编译,因此对于普通类型,它必须解析为定义的内容,而不执行任何操作仍然是定义的内容。只是让代码尽可能干净。单一责任,deallocator决定如何以及是否需要销毁对象 至于你的主要问题,你使用优化吗?这是第一个也是最重要的问题。任何没有优化的代码都可以保证正常工作。即使引用提供的复杂性对于未优化的代码也可能不同。您可以清楚地看到,第一次重新分配花费的时间几乎是原来的两倍,其余时间相当稳定
你是否有更好的时间与其他类型的操作?您是否尝试与普通阵列性能进行比较?除了Maciej的回答和Andy的评论之外,让我们检查生成的代码 使用此生成文件:
CXX = $(NDKPATH)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang++
CC = $(NDKPATH)/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang++
INC = -I$(NDKPATH)/cxx-stl/llvm-libc++/include/
LIB = -L$(NDKPATH)/cxx-stl/llvm-libc++/lib/
CXXFLAGS = -ggdb -O$(OPTLEVEL)
.PHONY: all clean dump
all: dump
dump: test
$(NDKPATH)/toolchains/llvm/prebuilt/linux-x86_64/aarch64-linux-android/bin/objdump -d -C test | gawk '/<big|<small|::resize/ {p=1} /^$$/ {p=0} {if (p) print $0}'
clean:
$(RM) test.o test
test: test.o
使用-O2
,编译器可以为我们进行大量优化
首先,resize()
完全消失了;它已被删除,因为没有人需要它了
big()。因为我没有运行过这段代码,所以我不能断言这对速度有多大帮助
small()。如果(v.begin!=v.end)v.end=v.begin
,它基本上变成了。这当然会非常快
ndk-vector-speed$ make clean && OPTLEVEL=2 make dump
rm -f test.o test
/home/snild/.androidsdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang++ -ggdb -O2 -c -o test.o test.cpp
/home/snild/.androidsdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang++ test.o -o test
/home/snild/.androidsdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/aarch64-linux-android/bin/objdump -d -C test | gawk '/<big|<small|::resize/ {p=1} /^$/ {p=0} {if (p) print }'
0000000000000e64 <big(std::__ndk1::vector<int, std::__ndk1::allocator<int> >&)>:
e64: a9402408 ldp x8, x9, [x0]
e68: 5292d00a mov w10, #0x9680 // #38528
e6c: 72a0130a movk w10, #0x98, lsl #16
e70: cb080129 sub x9, x9, x8
e74: 9342fd2b asr x11, x9, #2
e78: eb0a017f cmp x11, x10
e7c: 54000062 b.cs e88 <big(std::__ndk1::vector<int, std::__ndk1::allocator<int> >&)+0x24>
e80: cb0b0141 sub x1, x10, x11
e84: 14000011 b ec8 <std::__ndk1::vector<int, std::__ndk1::allocator<int> >::__append(unsigned long)>
e88: 528b400a mov w10, #0x5a00 // #23040
e8c: 72a04c4a movk w10, #0x262, lsl #16
e90: eb0a013f cmp x9, x10
e94: 540000a0 b.eq ea8 <big(std::__ndk1::vector<int, std::__ndk1::allocator<int> >&)+0x44>
e98: 528b4009 mov w9, #0x5a00 // #23040
e9c: 72a04c49 movk w9, #0x262, lsl #16
ea0: 8b090108 add x8, x8, x9
ea4: f9000408 str x8, [x0,#8]
ea8: d65f03c0 ret
0000000000000eac <small(std::__ndk1::vector<int, std::__ndk1::allocator<int> >&)>:
eac: a9402408 ldp x8, x9, [x0] // load the first two values (begin and end) from v
eb0: eb08013f cmp x9, x8 // compare them
eb4: 54000040 b.eq ebc <small(std::__ndk1::vector<int, std::__ndk1::allocator<int> >&)+0x10>
// skip to 'ret' if they were equal
eb8: f9000408 str x8, [x0,#8] // write v.begin to v.end
ebc: d65f03c0 ret // return.
ndk矢量速度$make clean&&OPTLEVEL=2 make dump
rm-f测试
/home/snild/.androidsdk/ndk bundle/toolschains/llvm/prebuild/linux-x86_64/bin/aarch64-linux-android29-clang++-ggdb-O2-c-o test.o test.cpp
/home/snild/.androidsdk/ndk bundle/toolschains/llvm/prebuild/linux-x86_64/bin/aarch64-linux-android29-clang++test.o-o测试
/home/snild/.androidsdk/ndk bundle/toolschains/llvm/prebuild/linux-x86_64/aarch64-linux-android/bin/objdump-d-C test | gawk'/感谢@snild Dolkow、@Maciej Załucki和@Andy Jewell
最终问题在优化级别
如果您使用CMake
,请使用此代码
target\u compile\u选项(opende专用
"$"
"$"
)
但选择您需要的优化级别
如果使用Application.mk
请使用此代码
“最大应为1微秒”什么是微秒?检查调整大小(0)
是否将容量更改为0。从技术上讲,它可以调整大小(0)
会使所有迭代器无效。顺便说一句,在测量时间时,您可能应该使用稳定时钟
(或者高分辨率时钟
,如果是稳定的
是真的
),您使用的优化级别是什么?这似乎是你在-O0时会看到的那种情况,但在-O2时可能会消失。@AndyJewell我如何检查实际的优化级别?根据我的gradle文件,它是标准的“getDefaultProguardFile('proguard-android.txt')”,从技术上讲,android NDK使用LLVM libc++,而不是GNU stdlibc++,但实现非常相似。这是和的相应来源。我已经再次发布了这个问题,但是有一点不同,你能看一下吗
#include <vector>
using std::vector;
void big(vector<int>& v) {
v.resize(10000000);
}
void small(vector<int>& v) {
v.resize(0);
}
int main() {
return 0;
}
ndk-vector-speed$ export NDKPATH=~/.androidsdk/ndk-bundle
ndk-vector-speed$ make clean && OPTLEVEL=0 make dump
rm -f test.o test
/home/snild/.androidsdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang++ -ggdb -O0 -c -o test.o test.cpp
/home/snild/.androidsdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang++ test.o -o test
/home/snild/.androidsdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/aarch64-linux-android/bin/objdump -d -C test | gawk '/<big|<small|::resize/ {p=1} /^$/ {p=0} {if (p) print }'
0000000000000f04 <big(std::__ndk1::vector<int, std::__ndk1::allocator<int> >&)>:
f04: d10083ff sub sp, sp, #0x20
f08: a9017bfd stp x29, x30, [sp,#16]
f0c: 910043fd add x29, sp, #0x10
f10: d292d001 mov x1, #0x9680 // #38528
f14: f2a01301 movk x1, #0x98, lsl #16
f18: f90007e0 str x0, [sp,#8]
f1c: f94007e0 ldr x0, [sp,#8]
f20: 94000013 bl f6c <std::__ndk1::vector<int, std::__ndk1::allocator<int> >::resize(unsigned long)>
f24: a9417bfd ldp x29, x30, [sp,#16]
f28: 910083ff add sp, sp, #0x20
f2c: d65f03c0 ret
0000000000000f30 <small(std::__ndk1::vector<int, std::__ndk1::allocator<int> >&)>:
f30: d10083ff sub sp, sp, #0x20
f34: a9017bfd stp x29, x30, [sp,#16]
f38: 910043fd add x29, sp, #0x10
f3c: d2800001 mov x1, #0x0 // #0
f40: f90007e0 str x0, [sp,#8]
f44: f94007e0 ldr x0, [sp,#8]
f48: 94000009 bl f6c <std::__ndk1::vector<int, std::__ndk1::allocator<int> >::resize(unsigned long)>
f4c: a9417bfd ldp x29, x30, [sp,#16]
f50: 910083ff add sp, sp, #0x20
f54: d65f03c0 ret
0000000000000f6c <std::__ndk1::vector<int, std::__ndk1::allocator<int> >::resize(unsigned long)>:
f6c: d100c3ff sub sp, sp, #0x30
f70: a9027bfd stp x29, x30, [sp,#32]
f74: 910083fd add x29, sp, #0x20
f78: f81f83a0 stur x0, [x29,#-8]
f7c: f9000be1 str x1, [sp,#16]
f80: f85f83a0 ldur x0, [x29,#-8]
f84: f90003e0 str x0, [sp]
f88: 94000020 bl 1008 <std::__ndk1::vector<int, std::__ndk1::allocator<int> >::size() const>
f8c: f90007e0 str x0, [sp,#8]
f90: f94007e0 ldr x0, [sp,#8]
f94: f9400be1 ldr x1, [sp,#16]
f98: eb01001f cmp x0, x1
f9c: 1a9f27e8 cset w8, cc
fa0: 37000048 tbnz w8, #0, fa8 <std::__ndk1::vector<int, std::__ndk1::allocator<int> >::resize(unsigned long)+0x3c>
fa4: 14000007 b fc0 <std::__ndk1::vector<int, std::__ndk1::allocator<int> >::resize(unsigned long)+0x54>
fa8: f9400be8 ldr x8, [sp,#16]
fac: f94007e9 ldr x9, [sp,#8]
fb0: eb090101 subs x1, x8, x9
fb4: f94003e0 ldr x0, [sp]
fb8: 9400001e bl 1030 <std::__ndk1::vector<int, std::__ndk1::allocator<int> >::__append(unsigned long)>
fbc: 14000010 b ffc <std::__ndk1::vector<int, std::__ndk1::allocator<int> >::resize(unsigned long)+0x90>
fc0: f94007e8 ldr x8, [sp,#8]
fc4: f9400be9 ldr x9, [sp,#16]
fc8: eb09011f cmp x8, x9
fcc: 1a9f97ea cset w10, hi
fd0: 3700004a tbnz w10, #0, fd8 <std::__ndk1::vector<int, std::__ndk1::allocator<int> >::resize(unsigned long)+0x6c>
fd4: 1400000a b ffc <std::__ndk1::vector<int, std::__ndk1::allocator<int> >::resize(unsigned long)+0x90>
fd8: b27e03e8 orr x8, xzr, #0x4
fdc: f94003e9 ldr x9, [sp]
fe0: f9400129 ldr x9, [x9]
fe4: f9400bea ldr x10, [sp,#16]
fe8: 9b0a7d08 mul x8, x8, x10
fec: 8b080128 add x8, x9, x8
ff0: f94003e0 ldr x0, [sp]
ff4: aa0803e1 mov x1, x8
ff8: 94000054 bl 1148 <std::__ndk1::vector<int, std::__ndk1::allocator<int> >::__destruct_at_end(int*)>
ffc: a9427bfd ldp x29, x30, [sp,#32]
1000: 9100c3ff add sp, sp, #0x30
1004: d65f03c0 ret
ndk-vector-speed$ make clean && OPTLEVEL=2 make dump
rm -f test.o test
/home/snild/.androidsdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang++ -ggdb -O2 -c -o test.o test.cpp
/home/snild/.androidsdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang++ test.o -o test
/home/snild/.androidsdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/aarch64-linux-android/bin/objdump -d -C test | gawk '/<big|<small|::resize/ {p=1} /^$/ {p=0} {if (p) print }'
0000000000000e64 <big(std::__ndk1::vector<int, std::__ndk1::allocator<int> >&)>:
e64: a9402408 ldp x8, x9, [x0]
e68: 5292d00a mov w10, #0x9680 // #38528
e6c: 72a0130a movk w10, #0x98, lsl #16
e70: cb080129 sub x9, x9, x8
e74: 9342fd2b asr x11, x9, #2
e78: eb0a017f cmp x11, x10
e7c: 54000062 b.cs e88 <big(std::__ndk1::vector<int, std::__ndk1::allocator<int> >&)+0x24>
e80: cb0b0141 sub x1, x10, x11
e84: 14000011 b ec8 <std::__ndk1::vector<int, std::__ndk1::allocator<int> >::__append(unsigned long)>
e88: 528b400a mov w10, #0x5a00 // #23040
e8c: 72a04c4a movk w10, #0x262, lsl #16
e90: eb0a013f cmp x9, x10
e94: 540000a0 b.eq ea8 <big(std::__ndk1::vector<int, std::__ndk1::allocator<int> >&)+0x44>
e98: 528b4009 mov w9, #0x5a00 // #23040
e9c: 72a04c49 movk w9, #0x262, lsl #16
ea0: 8b090108 add x8, x8, x9
ea4: f9000408 str x8, [x0,#8]
ea8: d65f03c0 ret
0000000000000eac <small(std::__ndk1::vector<int, std::__ndk1::allocator<int> >&)>:
eac: a9402408 ldp x8, x9, [x0] // load the first two values (begin and end) from v
eb0: eb08013f cmp x9, x8 // compare them
eb4: 54000040 b.eq ebc <small(std::__ndk1::vector<int, std::__ndk1::allocator<int> >&)+0x10>
// skip to 'ret' if they were equal
eb8: f9000408 str x8, [x0,#8] // write v.begin to v.end
ebc: d65f03c0 ret // return.
target_compile_options(opende PRIVATE
"$<$<CONFIG:RELEASE>:-O3>"
"$<$<CONFIG:DEBUG>:-O3>"
)