2017-02-01 15 views
11

जीसीसी 4.9 का उपयोग करना, Linaro toolchain का उपयोग कर एआरएम के लिए पार संकलन, मैं vector.assign() परिवर्तन की संकलित परिणाम जब -std=c++14 जोड़ने में पाया है, एक तरीका है जो महत्व प्रदर्शन समस्याओं को बनाता है।std :: वेक्टर <uint8_t> मैन्युअल बजाय memcpy बुलाने की प्रतिलिपि बनाई जा रही है जब सी ++ 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); 
} 

जीसीसी झंडे:

असेंबली को देखते हुए, मैं देख सकता हूं कि मूल संस्करण (सी ++ 03, मुझे लगता है?) memmove कहता है, जबकि सी ++ 14 संस्करण इसके बजाय एक अतिरिक्त लूप जोड़ता है जो ऐसा लगता है कि डेटा कॉपी करना है मैन्युअल रूप से। .loc टैग देखकर gcc -fverbose-asm के साथ जोड़ता है, इस लूप में दिए गए निर्देश stl_construct.h और stl_uninitialized.h से आते हैं।

जीसीसी 5.2.1 (सी ++ 14 के साथ) में बदलना, यह memmove के बजाय memcpy को छोड़कर, सी ++ 03 उदाहरण के लगभग समान रूप से संकलित करता है।

मैं के बजाय std::unique_ptr<uint8_t[]> का उपयोग करके इस समस्या को हल करने में सक्षम हूं। हालांकि, मैं इस मुद्दे के निचले हिस्से में यह जानना चाहता हूं कि vector एस का उपयोग करने वाले अन्य स्थानों में प्रदर्शन समस्याएं हो सकती हैं और उन्हें संभावित रूप से कैसे ठीक किया जा सकता है (जीसीसी 5.2 में अपडेट करना व्यावहारिक नहीं है)।

तो मेरा प्रश्न है: यह सी ++ 11/14 के तहत अलग-अलग संकलन क्यों करता है?

संदर्भ के लिए, gcc --version रिपोर्ट:
arm-linux-gnueabihf-gcc (Linaro GCC 4.9-2014.12) 4.9.3 20141205 (prerelease)

यहाँ विधानसभा उत्पन्न जीसीसी है:

# 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

शायद यह कोई फर्क नहीं पड़ता है, लेकिन आरंभ करने के बजाय क्यों असाइन करें? उत्तरार्द्ध एक आवंटन से बच सकता है। 'वेक्टरवापर_टी (uint8_t const * pData, size_t लंबाई): डेटा (पीडीटा, पीडीटा + लंबाई) {}' – juanchopanza

+0

यह प्रश्न लिनारो बग रिपोर्ट के लिए बेहतर अनुकूल लगता है। – ildjarn

+3

2 संभावनाएं: या तो कोड समान होता है, लेकिन जीसीसी -5 + यह पता लगाता है कि लूप memcpy के बराबर है और इसे memcpy पर कॉल के साथ बदल देता है, या libstdC++ ने कुछ नया विशेष पथ प्राप्त किया जो memcpy को कॉल करता है। '-फडम्प-पेड़-सब' पास करने का प्रयास करें और यह निर्धारित करने के लिए कि क्या हो रहा है, कुछ शुरुआती/देर से डंप देखें। –

उत्तर

10

यह 4.9.2 रिलीज में एक जीसीसी बग था, PR 64476 देखते हैं। डिफ़ॉल्ट -std=gnu++03 मोड और -std=c++14 के बीच का अंतर यह है कि सी ++ 11 के लिए और बाद में यह छोटे प्रकार के होते हैं जो असाइन करने योग्य नहीं होते हैं (क्योंकि उनके पास एक हटाए गए असाइनमेंट ऑपरेटर हो सकते हैं) जो std::uninitialized_copy के कार्यान्वयन का कारण बनता है (धीमा) कोड पथ। असाइन करने योग्यता की जांच गलत थी, जिसका अर्थ है कि जब हमें आवश्यकता नहीं थी तो हमने धीमा रास्ता लिया।

मैंने इसे दो साल पहले जीसीसी 4.9.3 के लिए तय किया था, लेकिन आपका कंपाइलर 4.9.2 और 4.9.3 रिलीज के बीच किए गए स्नैपशॉट पर आधारित है और ठीक होने के लिए कुछ सप्ताह पुराना है।

आप लिनारो से अपने जीसीसी 4.9 कंपाइलर को 4.9.4, या कम से कम इस बग को ठीक करने के लिए पैच को लागू करने के लिए कह सकते हैं।

संबंधित मुद्दे