Dưới đây là một số mã (chương trình đầy đủ sau sau trong câu hỏi):Tối ưu hóa bộ phận trong gcc
template <typename T>
T fizzbuzz(T n) {
T count(0);
#if CONST
const T div(3);
#else
T div(3);
#endif
for (T i(0); i <= n; ++i) {
if (i % div == T(0)) count += i;
}
return count;
}
Bây giờ, nếu tôi gọi mẫu chức năng này với int
, sau đó tôi nhận được một yếu tố của sự khác biệt 6 hiệu suất theo liệu tôi xác định cONST hay không:
$ gcc --version
gcc (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)
$ make -B wrappedint CPPFLAGS="-O3 -Wall -Werror -DWRAP=0 -DCONST=0" &&
time ./wrappedint
g++ -O3 -Wall -Werror -DWRAP=0 -DCONST=0 wrappedint.cpp -o wrappedi
nt
484573652
real 0m2.543s
user 0m2.059s
sys 0m0.046s
$ make -B wrappedint CPPFLAGS="-O3 -Wall -Werror -DWRAP=0 -DCONST=1" &&
time ./wrappedint
g++ -O3 -Wall -Werror -DWRAP=0 -DCONST=1 wrappedint.cpp -o wrappedi
nt
484573652
real 0m0.655s
user 0m0.327s
sys 0m0.046s
Kiểm tra tháo gỡ cho thấy rằng trong (const) nhanh vụ án, modulo đã được biến thành một nhân và thay đổi loại điều, trong khi ở chậm (không const) trường hợp nó đang sử dụng idivl
.
Thậm chí tệ hơn, nếu tôi cố gắng bọc số nguyên trong một lớp, thì tối ưu hóa không xảy ra cho dù tôi có sử dụng const hay không. Mã này luôn luôn sử dụng idivl
và chạy chậm:
#include <iostream>
struct WrappedInt {
int v;
explicit WrappedInt(const int &val) : v(val) {}
bool operator<=(const WrappedInt &rhs) const { return v <= rhs.v; }
bool operator==(const WrappedInt &rhs) const { return v == rhs.v; }
WrappedInt &operator++() { ++v; return *this; }
WrappedInt &operator+=(const WrappedInt &rhs) { v += rhs.v; return *this; }
WrappedInt operator%(const WrappedInt &rhs) const
{ return WrappedInt(v%rhs.v); }
};
std::ostream &operator<<(std::ostream &s, WrappedInt w) {
return s << w.v;
}
template <typename T>
T fizzbuzz(T n) {
T count(0);
#if CONST
const T div(3);
#else
T div(3);
#endif
for (T i(0); i <= n; ++i) {
if (i % div == T(0)) count += i;
}
return count;
}
int main() {
#if WRAP
WrappedInt w(123456789);
std::cout << fizzbuzz(w) << "\n";
#else
std::cout << fizzbuzz<int>(123456789) << "\n";
#endif
}
Câu hỏi của tôi là:
1) Có một nguyên tắc đơn giản của C++ chính nó, hoặc tối ưu hóa gcc, mà giải thích lý do tại sao điều này xảy ra, hoặc là nó chỉ là một trường hợp của "heuristics chạy khác nhau, đây là mã bạn nhận được"?
2) Có cách nào để làm cho trình biên dịch nhận ra rằng trình bao bọc địa phương được khai báo và không bao giờ tham chiếu const WrappedInt của tôi có thể được coi là giá trị const thời gian biên dịch không? Tôi muốn điều này là một thay thế thẳng cho int trong mẫu.
3) Có cách nào được biết để gói một int sao cho trình biên dịch có thể hủy gói khi tối ưu hóa không? Mục tiêu là WrappedInt sẽ là một mẫu dựa trên chính sách. Nhưng nếu một chính sách "không làm gì" dẫn đến các hình phạt 6x tùy ý về tốc độ tùy ý trên int, thì tốt hơn là nên loại bỏ đặc biệt tình huống đó và sử dụng trực tiếp int.
Trong trường hợp nó gây ra bất kỳ sự nhầm lẫn nào - tôi có lẽ đã đổi tên hàm "fizz" khi tôi xóa "|| i% 5" ;-) –