Android NDK:vector.resize()太慢,与分配有关吗?

我在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",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

所以,首先

  1. 如您所见,仅resize(0)我就得到550毫秒...应该是 最多1微秒而不是MILLI
  2. 其次是为什么resize(size)再次达到550毫秒 载体的容量是否不变?

这是2种非常奇怪的行为。

我们欢迎您采用这段代码,如果您自己不相信我,请检查一下自己:) 但是只要在Android NDK上签入 ,而不是在Visual Studio上签入项目,因为它像它应该的那样工作。

这真的像是虫子...

还是我做错了什么?

编辑

我检查了是否采用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

如果您不持有具有析构函数的平凡对象也没有问题,但是如果您持有矢量(如我的情况)那样的琐碎且没有析构函数的int对象,那么...是非常奇怪的行为,如何从实际上没有析构函数的对象中调用析构函数?

看起来像编译器错误吗?

yang880912 回答:Android NDK:vector.resize()太慢,与分配有关吗?

首先,许多库功能的实现强烈依赖于编译器优化。删除容器中的对象可以调用destroy,而对于易碎对象而言,destroy不会做任何事情。如果不执行任何操作,则编译器将优化所有逻辑。破坏STL just take a look中的对象涉及很多逻辑。从本质上讲,destroy被调用以确保其处理所有情况,包括自定义分配器。它必须编译,因此对于琐碎的类型,它必须解析为已定义的内容,而仍然不执行任何操作。只是为了使代码尽可能干净。单一责任,deallocator决定如何以及是否需要销毁对象。

关于您的主要问题,您是否使用优化?这是第一个也是最重要的问题。任何未经优化的代码都可以保证正常工作。对于未优化的代码,即使参考提供的复杂性也可能不同。您可以清楚地看到,第一次重新分配所花的时间几乎是原来的两倍,其余时间则相当稳定。

使用此类型的其他操作,您的时间是否更好?您是否尝试将其与普通阵列的性能进行比较?

,

感谢@Snild Dolkow,@ MaciejZałucki和@Andy Jewell

最终问题出在优化级别

https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

如果您使用CMake,请使用此代码

https://stackoverflow.com/a/45333618/5709159

target_compile_options(opende PRIVATE
"$<$<CONFIG:RELEASE>:-O3>"
"$<$<CONFIG:DEBUG>:-O3>"
)

但是选择所需的优化级别

如果您使用Application.mk,请使用此代码

https://stackoverflow.com/a/18433696/5709159

,

在Maciej的答案和Andy的评论中,让我们检查生成的代码。

使用此Makefile:

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

...和一个非常简单的test.cpp:

#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;
}

未经优化的编译(-O0),请注意big()small()都如何调用resize(),这会在循环中完成很多工作(如您所也可以在源代码中找到。)

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,#0x10
     f10:   d292d001    mov x1,#0x9680                 // #38528
     f14:   f2a01301    movk    x1,#0x98,lsl #16
     f18:   f90007e0    str x0,#8]
     f1c:   f94007e0    ldr x0,#8]
     f20:   94000013    bl  f6c <std::__ndk1::vector<int,std::__ndk1::allocator<int> >::resize(unsigned long)>
     f24:   a9417bfd    ldp x29,#16]
     f28:   910083ff    add sp,#0x20
     f2c:   d65f03c0    ret
0000000000000f30 <small(std::__ndk1::vector<int,std::__ndk1::allocator<int> >&)>:
     f30:   d10083ff    sub sp,#0x20
     f34:   a9017bfd    stp x29,#16]
     f38:   910043fd    add x29,#0x10
     f3c:   d2800001    mov x1,#0x0                    // #0
     f40:   f90007e0    str x0,#8]
     f44:   f94007e0    ldr x0,#8]
     f48:   94000009    bl  f6c <std::__ndk1::vector<int,std::__ndk1::allocator<int> >::resize(unsigned long)>
     f4c:   a9417bfd    ldp x29,#16]
     f50:   910083ff    add sp,#0x20
     f54:   d65f03c0    ret
0000000000000f6c <std::__ndk1::vector<int,std::__ndk1::allocator<int> >::resize(unsigned long)>:
     f6c:   d100c3ff    sub sp,#0x30
     f70:   a9027bfd    stp x29,#32]
     f74:   910083fd    add x29,#0x20
     f78:   f81f83a0    stur    x0,[x29,#-8]
     f7c:   f9000be1    str x1,#16]
     f80:   f85f83a0    ldur    x0,#-8]
     f84:   f90003e0    str x0,[sp]
     f88:   94000020    bl  1008 <std::__ndk1::vector<int,std::__ndk1::allocator<int> >::size() const>
     f8c:   f90007e0    str x0,#8]
     f90:   f94007e0    ldr x0,#8]
     f94:   f9400be1    ldr x1,#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,#16]
     fac:   f94007e9    ldr x9,#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,#8]
     fc4:   f9400be9    ldr x9,#16]
     fc8:   eb09011f    cmp x8,x9
     fcc:   1a9f97ea    cset    w10,hi
     fd0:   3700004a    tbnz    w10,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,#16]
     fe8:   9b0a7d08    mul 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,#32]
    1000:   9100c3ff    add sp,#0x30
    1004:   d65f03c0    ret

借助-O2,编译器可以为我们做很多优化。

首先,resize()完全消失了;它已被删除,因为没有人需要它。

big()内联了resize()中的需求,直接调用了__append(),并且看起来比我们之前调用的完整resize()函数更简单。由于我尚未运行此代码,因此无法就此对速度有多大帮助做出任何主张。

small()现在没有函数调用,没有循环,并且只有五个指令(我在下面手动注释了)。它实际上已变成if (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,[x0]
     e68:   5292d00a    mov w10,#0x9680                    // #38528
     e6c:   72a0130a    movk    w10,lsl #16
     e70:   cb080129    sub x9,x8
     e74:   9342fd2b    asr x11,#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,lsl #16
     ea0:   8b090108    add 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,[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,#8]   // write v.begin to v.end
     ebc:   d65f03c0    ret               // return.

结论:Maciej和Andy是正确的;您没有在启用优化的情况下进行构建。

本文链接:https://www.f2er.com/3146245.html

大家都在问