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.
Đây là một câu hỏi thú vị, ngay cả khi nó ** là ** tối ưu hóa vi mô. – jpm
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ó. –
@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ò :) –