2017-02-01 2 views
11

Использование gcc 4.9, кросс-компиляция для ARM с использованием инструментальной привязки Linaro, я нашел скомпилированный результат из vector.assign() изменений при добавлении -std=c++14, в способ, который создает проблемы с производительностью значимости.std :: vector <uint8_t> ручное копирование вместо вызова memcpy при включенном C++ 11/14

Я пробовал несколько разных способов сделать это распределение + копия, но все они имеют эту проблему с производительностью, если я использую std::vector, чтобы сделать это.

я могу воспроизвести проблему с этой игрушкой, например:

VectorTest.h

#include <stdint.h> 
#include <stddef.h> 
#include <vector> 

struct VectorWrapper_t 
{ 
    VectorWrapper_t(uint8_t const* pData, size_t length); 
    std::vector<uint8_t> data; 
}; 

VectorTest.cpp

#include "VectorTest.h" 

VectorWrapper_t::VectorWrapper_t(uint8_t const* pData, size_t length) 
{ 
    data.assign(pData, pData + length); 
} 

НКУ флаги:

-std=c++14 \ 
-mthumb -march=armv7-a -mtune=cortex-a9 \ 
-mlittle-endian -mfloat-abi=hard -mfpu=neon -Wa,-mimplicit-it=thumb \ 
-O2 -g 

Просмотр сборки, я могу понять, почему исходная версия (C++ 03, я предполагаю?) Вызывает memmove, тогда как версия C++ 14 вместо этого добавляет дополнительный цикл, который выглядит как копирование данных вручную. Глядя на теги .loc, gcc добавляет с -fverbose-asm, инструкции в этом цикле исходят от stl_construct.h и stl_uninitialized.h.

Переход на gcc 5.2.1 (с C++ 14), он компилируется почти идентично примеру C++ 03, за исключением memcpy вместо memmove.

Я могу обойти эту проблему, используя std::unique_ptr<uint8_t[]> вместо vector здесь. Тем не менее, я хотел бы разобраться в этом вопросе, чтобы выяснить, могут ли проблемы с производительностью и местами, использующими vector s, и как их исправить (обновление до gcc 5.2 нецелесообразно).

Так что мой вопрос: Почему он компилируется по-разному под C++ 11/14?

Для справки, gcc --version отчеты:
arm-linux-gnueabihf-gcc (Linaro GCC 4.9-2014.12) 4.9.3 20141205 (prerelease).

Вот сборка GCC генерируется:

# C++03, gcc 4.9 

    push {r3, r4, r5, r6, r7, lr} @ 
    movs r3, #0 @ tmp118, 
    mov r4, r0 @ this, this 
    str r3, [r0] @ tmp118, MEM[(struct _Vector_impl *)this_1(D)]._M_start 
    mov r5, r2 @ length, length 
    str r3, [r0, #4] @ tmp118, MEM[(struct _Vector_impl *)this_1(D)]._M_finish 
    str r3, [r0, #8] @ tmp118, MEM[(struct _Vector_impl *)this_1(D)]._M_end_of_storage 
    cbnz r2, .L19 @ length, 
    mov r0, r4 @, this 
    pop {r3, r4, r5, r6, r7, pc} @ 
.L19: 
    mov r0, r2 @, length 
    mov r6, r1 @ pData, pData 
    bl _Znwj @ 
    mov r2, r5 @, length 
    mov r1, r6 @, pData 
    mov r7, r0 @ D.13516, 
    bl memmove @ 
    ldr r0, [r4] @ D.13515, MEM[(struct vector *)this_1(D)].D.11902._M_impl._M_start 
    cbz r0, .L3 @ D.13515, 
    bl _ZdlPv @ 
.L3: 
    add r5, r5, r7 @ D.13515, D.13516 
    str r7, [r4] @ D.13516, MEM[(struct vector *)this_1(D)].D.11902._M_impl._M_start 
    str r5, [r4, #4] @ D.13515, MEM[(struct vector *)this_1(D)].D.11902._M_impl._M_finish 
    mov r0, r4 @, this 
    str r5, [r4, #8] @ D.13515, MEM[(struct vector *)this_1(D)].D.11902._M_impl._M_end_of_storage 
    pop {r3, r4, r5, r6, r7, pc} @ 
.L6: 
    ldr r0, [r4] @ D.13515, MEM[(struct _Vector_base *)this_1(D)]._M_impl._M_start 
    cbz r0, .L5 @ D.13515, 
    bl _ZdlPv @ 
.L5: 
    bl __cxa_end_cleanup @ 

# C++14, gcc 4.9 

    push {r3, r4, r5, r6, r7, lr} @ 
    movs r3, #0 @ tmp157, 
    mov r6, r0 @ this, this 
    str r3, [r0] @ tmp157, MEM[(struct _Vector_impl *)this_1(D)]._M_start 
    mov r5, r2 @ length, length 
    str r3, [r0, #4] @ tmp157, MEM[(struct _Vector_impl *)this_1(D)]._M_finish 
    str r3, [r0, #8] @ tmp157, MEM[(struct _Vector_impl *)this_1(D)]._M_end_of_storage 
    cbnz r2, .L25 @ length, 
    mov r0, r6 @, this 
    pop {r3, r4, r5, r6, r7, pc} @ 
.L25: 
    mov r0, r2 @, length 
    mov r4, r1 @ pData, pData 
    bl _Znwj @ 
    adds r3, r4, r5 @ D.20345, pData, length 
    mov r7, r0 @ __result, 
    cmp r4, r3 @ pData, D.20345 
    ittt ne 
    addne r1, r4, #-1 @ ivtmp.76, pData, 
    movne r3, r0 @ __result, __result 
    addne r4, r0, r5 @ D.20346, __result, length 
    beq .L26 @, 
.L7: 
    ldrb r2, [r1, #1]! @ zero_extendqisi2 @ D.20348, MEM[base: _48, offset: 0] 
    cbz r3, .L6 @ __result, 
    strb r2, [r3] @ D.20348, MEM[base: __result_23, offset: 0B] 
.L6: 
    adds r3, r3, #1 @ __result, __result, 
    cmp r3, r4 @ __result, D.20346 
    bne .L7 @, 
.L8: 
    ldr r0, [r6] @ D.20346, MEM[(struct vector *)this_1(D)].D.18218._M_impl._M_start 
    cbz r0, .L5 @ D.20346, 
    bl _ZdlPv @ 
.L5: 
    str r7, [r6] @ __result, MEM[(struct vector *)this_1(D)].D.18218._M_impl._M_start 
    mov r0, r6 @, this 
    str r4, [r6, #4] @ D.20346, MEM[(struct vector *)this_1(D)].D.18218._M_impl._M_finish 
    str r4, [r6, #8] @ D.20346, MEM[(struct vector *)this_1(D)].D.18218._M_impl._M_end_of_storage 
    pop {r3, r4, r5, r6, r7, pc} @ 
.L26: 
    adds r4, r0, r5 @ D.20346, __result, length 
    b .L8 @ 
.L11: 
    ldr r0, [r6] @ D.20346, MEM[(struct _Vector_base *)this_1(D)]._M_impl._M_start 
    cbz r0, .L10 @ D.20346, 
    bl _ZdlPv @ 
.L10: 
    bl __cxa_end_cleanup @ 

# C++14, gcc 5.2 

    push {r3, r4, r5, r6, r7, lr} @ 
    movs r3, #0 @ tmp118, 
    mov r4, r0 @ this, this 
    str r3, [r0] @ tmp118, MEM[(struct _Vector_impl *)this_1(D)]._M_start 
    str r3, [r0, #4] @ tmp118, MEM[(struct _Vector_impl *)this_1(D)]._M_finish 
    str r3, [r0, #8] @ tmp118, MEM[(struct _Vector_impl *)this_1(D)]._M_end_of_storage 
    cbnz r2, .L19 @ length, 
    mov r0, r4 @, this 
    pop {r3, r4, r5, r6, r7, pc} @ 
.L19: 
    mov r0, r2 @, length 
    mov r6, r1 @ pData, pData 
    mov r5, r2 @ length, length 
    bl _Znwj @ 
    mov r2, r5 @, length 
    mov r1, r6 @, pData 
    mov r7, r0 @ D.20824, 
    bl memcpy @ 
    ldr r0, [r4] @ D.20823, MEM[(struct vector *)this_1(D)].D.18751._M_impl._M_start 
    cbz r0, .L3 @ D.20823, 
    bl _ZdlPv @ 
.L3: 
    add r5, r5, r7 @ D.20823, D.20824 
    str r7, [r4] @ D.20824, MEM[(struct vector *)this_1(D)].D.18751._M_impl._M_start 
    str r5, [r4, #4] @ D.20823, MEM[(struct vector *)this_1(D)].D.18751._M_impl._M_finish 
    mov r0, r4 @, this 
    str r5, [r4, #8] @ D.20823, MEM[(struct vector *)this_1(D)].D.18751._M_impl._M_end_of_storage 
    pop {r3, r4, r5, r6, r7, pc} @ 
.L6: 
    ldr r0, [r4] @ D.20823, MEM[(struct _Vector_base *)this_1(D)]._M_impl._M_start 
    cbz r0, .L5 @ D.20823, 
    bl _ZdlPv @ 
.L5: 
    bl __cxa_end_cleanup @ 
+9

Это, вероятно, не будет иметь большого значения, но зачем назначать вместо инициализации? Последний мог избежать одного распределения. 'VectorWrapper_t (uint8_t const * pData, size_t length): data (pData, pData + length) {}' – juanchopanza

+0

Этот вопрос кажется более подходящим для отчета об ошибке Linaro. – ildjarn

+3

2 возможности: либо код начинается, но gcc-5 + обнаруживает, что цикл эквивалентен memcpy и заменяет его вызовом memcpy, или libstdC++ получил новый специальный путь, который вызывает memcpy. Попробуйте передать '-fdump-tree-all' и посмотрите на ранние/поздние дампы, чтобы определить, что происходит. –

ответ

10

Это была ошибка GCC в релизе 4.9.2, см PR 64476. Разница между режимом -std=gnu++03 по умолчанию и -std=c++14 заключается в том, что для C++ 11 и более поздних версий возможно иметь тривиальные типы, которые не могут быть назначены (поскольку они могут иметь удаленный оператор присваивания), что приводит к тому, что реализация std::uninitialized_copy принимает другую (медленнее). Проверка на назначение была неправильной, что означает, что мы взяли медленный путь, когда нам не нужно.

Я установил его два года назад для GCC 4.9.3, но ваш компилятор основан на моментальном снимке, сделанном между версиями 4.9.2 и 4.9.3, и на несколько недель слишком стар, чтобы иметь исправление.

Вы можете попросить Linaro обновить компилятор GCC 4.9 до 4.9.4 или, по крайней мере, применить исправление, исправляющее эту ошибку.

Смежные вопросы