Tôi đã trả lời this question và nhận thấy những gì tôi coi là hành vi lạ của trình biên dịch.con trỏ trên con trỏ - lý do cho hình phạt hiệu suất
đầu tiên tôi đã viết chương trình này (như là một phần của câu trả lời của tôi ở đó):
class Vector {
private:
double** ptr;
public:
Vector(double** _ptr): ptr(_ptr) {}
inline double& operator[](const int iIndex) const {
return *ptr[iIndex];
}
};
extern "C" int test(const double a);
int main() {
double a[2] = { 1.0, 2.0 };
Vector va((double**) &a);
double a1 = va[0];
test(a1);
double a2 = va[0];
test(a2);
}
mà tạo ra hai hướng dẫn tải khi biên soạn với:
clang -O3 -S -emit-llvm main.cpp -o main.ll
Điều này có thể được nhìn thấy trong llvm-IR (và có thể được nhìn thấy trong hội đồng):
define i32 @main() #0 { entry: %a.sroa.0.0.copyload = load double*, double** bitcast ([2 x double]* @_ZZ4mainE1a to double**), align 16 %0 = load double, double* %a.sroa.0.0.copyload, align 8, !tbaa !2 %call1 = tail call i32 @test(double %0) %1 = load double, double* %a.sroa.0.0.copyload, align 8, !tbaa !2 %call3 = tail call i32 @test(double %1) ret i32 0 }
Tôi chỉ mong đợi một hướng dẫn tải, vì không chức năng với tác dụng phụ trên bộ nhớ đã được gọi, và tôi đã không liên kết đối tượng này với một cái gì đó với các tác dụng phụ. Trên thực tế, khi đọc chương trình, tôi chỉ mong đợi hai cuộc gọi đến
test(1.0);
vì mảng của tôi không đổi trong bộ nhớ và mọi thứ có thể được in đúng cách.
Chỉ để chắc chắn, tôi đã thay thế con trỏ đôi bởi một con trỏ đơn giản:
class Vector {
private:
double* ptr;
public:
Vector(double* _ptr): ptr(_ptr) {}
inline double& operator[](const int iIndex) const {
return ptr[iIndex];
}
};
extern "C" int test(const double a);
int main() {
double a[2] = { 1.0, 2.0 };
Vector va(a);
double a1 = va[0];
test(a1);
double a2 = va[0];
test(a2);
}
Biên soạn với cùng một dòng, tôi nhận được kết quả mong đợi:
define i32 @main() #0 {
entry:
%call1 = tail call i32 @test(double 1.000000e+00)
%call3 = tail call i32 @test(double 1.000000e+00)
ret i32 0
}
nào trông giống như cách tốt hơn tối ưu :)
câu hỏi của tôi do đó là:
Lý do nào ngăn trình biên dịch thực hiện cùng nội tuyến trên mẫu mã đầu tiên? Đó có phải là con trỏ kép không?
Một con trỏ tới một mảng của 'double' là * không * giống như một con trỏ đến một con trỏ đến một gấp đôi. Khi bạn thực hiện '& a' bạn nhận được một cái gì đó của kiểu' double (*) [2] '. Nếu bạn cần phải tạo kiểu C trong C++, thì đó là dấu hiệu của một thứ gì đó không hoàn toàn đúng. –
Cảm ơn bạn đã tăng điểm quan trọng. Điều đó giải thích bất cứ điều gì về phần còn lại của chương trình? –
Trong mã đầu tiên của bạn, bạn đang cố gắng truy cập bộ nhớ bên ngoài những gì trình biên dịch có thể thấy, vì vậy không có lý do nào trình biên dịch giả định rằng 'test' không sửa đổi vị trí bộ nhớ này. – Holt