2011-06-21 13 views
11

Tôi biết rằng các phiên bản hậu tố của toán tử tăng/giảm sẽ thường được tối ưu hóa bởi trình biên dịch cho các kiểu tích hợp (tức là không có bản sao nào được tạo), nhưng trường hợp này là iterator s? Chúng chỉ là những toán tử bị quá tải, và có thể được thực hiện theo nhiều cách, nhưng vì hành vi của chúng được xác định một cách nghiêm ngặt, có thể chúng được tối ưu hóa, và nếu vậy, chúng là bởi bất kỳ/nhiều trình biên dịch nào không? Không.Liệu sự kém hiệu quả của các toán tử postfix ++/- có được tối ưu hóa cho các trình lặp STL không?

#include <vector> 

void foo(std::vector<int>& v){ 
    for (std::vector<int>::iterator i = v.begin(); 
     i!=v.end(); 
     i++){ //will this get optimised by the compiler? 
    *i += 20; 
    } 
} 
+0

Đây là một câu hỏi thú vị, ngay cả khi nó ** là ** tối ưu hóa vi mô. – jpm

+0

trừ khi các phép toán vòng lặp có các tác dụng phụ, trình biên dịch được phép tối ưu hóa phiên bản postincrement sau * as-if * rule. Cho dù nó có hay không phụ thuộc vào trình biên dịch. Indebug xây dựng, nó có thể sẽ không được tối ưu hóa, vậy tại sao làm cho gỡ lỗi chậm hơn? Tôi không thấy một vấn đề phát triển một thói quen tốt khi sử dụng postincrement chỉ khi bạn thực sự cần nó. –

+0

@Gene Tôi đồng ý và tôi có thói quen sử dụng tiền gia tăng bất cứ khi nào có thể. Tôi chỉ tò mò :) –

Trả lời

9

Trong trường hợp cụ thể của std::vector tình hình thực hiện STL GNU GCC (phiên bản 4.6.1), tôi không nghĩ rằng sẽ có một sự khác biệt hiệu suất về mức tối ưu hóa đủ cao.

Việc triển khai cho các trình vòng lặp chuyển tiếp trên vector được cung cấp bởi __gnu_cxx::__normal_iterator<typename _Iterator, typename _Container>. Chúng ta hãy nhìn vào constructor của nó và postfix ++ điều hành:

explicit 
    __normal_iterator(const _Iterator& __i) : _M_current(__i) { } 

    __normal_iterator 
    operator++(int) 
    { return __normal_iterator(_M_current++); } 

Và instantiation của nó trong vector:

typedef __gnu_cxx::__normal_iterator<pointer, vector> iterator; 

Như bạn có thể thấy, nó trong nội bộ thực hiện một increment postfix trên một con trỏ bình thường, sau đó vượt qua gốc giá trị thông qua hàm tạo riêng của nó, nó sẽ lưu nó vào một thành viên cục bộ. Mã này nên tầm thường để loại bỏ thông qua phân tích giá trị chết.

Nhưng nó có được tối ưu hóa thực sự không? Hãy cùng tìm hiểu. mã kiểm tra:

#include <vector> 

void test_prefix(std::vector<int>::iterator &it) 
{ 
    ++it; 
} 

void test_postfix(std::vector<int>::iterator &it) 
{ 
    it++; 
} 

lắp ráp Output (trên -Os):

.file "test.cpp" 
    .text 
    .globl _Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE 
    .type _Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE, @function 
_Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE: 
.LFB442: 
    .cfi_startproc 
    pushl %ebp 
    .cfi_def_cfa_offset 8 
    .cfi_offset 5, -8 
    movl %esp, %ebp 
    .cfi_def_cfa_register 5 
    movl 8(%ebp), %eax 
    addl $4, (%eax) 
    popl %ebp 
    .cfi_def_cfa 4, 4 
    .cfi_restore 5 
    ret 
    .cfi_endproc 
.LFE442: 
    .size _Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE, .-_Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE 
    .globl _Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE 
    .type _Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE, @function 
_Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE: 
.LFB443: 
    .cfi_startproc 
    pushl %ebp 
    .cfi_def_cfa_offset 8 
    .cfi_offset 5, -8 
    movl %esp, %ebp 
    .cfi_def_cfa_register 5 
    movl 8(%ebp), %eax 
    addl $4, (%eax) 
    popl %ebp 
    .cfi_def_cfa 4, 4 
    .cfi_restore 5 
    ret 
    .cfi_endproc 
.LFE443: 
    .size _Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE, .-_Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE 
    .ident "GCC: (Debian 4.6.0-10) 4.6.1 20110526 (prerelease)" 
    .section .note.GNU-stack,"",@progbits 

Như bạn có thể thấy, chính xác cùng một assembly là đầu ra trong cả hai trường hợp.

Tất nhiên, điều này có thể không nhất thiết phải là trường hợp cho các trình vòng lặp tùy chỉnh hoặc các loại dữ liệu phức tạp hơn. Nhưng có vẻ như, đối với vector cụ thể, tiền tố và postfix (mà không nắm bắt giá trị trả về hậu tố) có hiệu suất giống nhau.

+0

Phân tích tốt. Tôi hy vọng rằng bất kỳ trình biên dịch tối ưu hóa tốt sẽ thực hiện tương tự. –

+0

Cảm ơn câu trả lời đó. Tôi nghi ngờ @Mark là đúng, mặc dù tôi tò mò về các trình biên dịch khác. –

+0

Hãy thử nghiệm với các trình biên dịch khác :) – bdonlan

Các vấn đề liên quan