2017-10-16 54 views
7

Phiên bản đầu tiên thực hiện tối ưu hóa bằng cách di chuyển một giá trị từ bộ nhớ sang biến cục bộ. Phiên bản thứ hai thì không.Tại sao trình biên dịch ghi biến thành viên vào bộ nhớ cho mỗi lần lặp của vòng lặp này?

Tôi đã mong đợi trình biên dịch có thể chọn thực hiện tối ưu hóa localValue ở đây và không đọc và ghi giá trị từ bộ nhớ cho mỗi lần lặp của vòng lặp. Tại sao không?

class Example 
{ 
    public: 
     void processSamples(float * x, int num) 
     { 
      float localValue = v1; 

      for (int i = 0; i < num; ++i) 
      { 
       x[i] = x[i] + localValue; 
       localValue = 0.5 * x[i]; 
      } 

      v1 = localValue; 
     } 

     void processSamples2(float * x, int num) 
     { 

      for (int i = 0; i < num; ++i) 
      { 
       x[i] = x[i] + v1; 
       v1 = 0.5 * x[i]; 
      } 

     } 

    float v1; 
}; 

processSamples lắp ráp mã như thế này:

.L4: 
    addss xmm0, DWORD PTR [rax] 
    movss DWORD PTR [rax], xmm0 
    mulss xmm0, xmm1 
    add rax, 4 
    cmp rax, rcx 
    jne .L4 

processSamples2 này:

.L5: 
    movss xmm0, DWORD PTR [rax] 
    addss xmm0, DWORD PTR example[rip] 
    movss DWORD PTR [rax], xmm0 
    mulss xmm0, xmm1 
    movss DWORD PTR example[rip], xmm0 
    add rax, 4 
    cmp rax, rdx 
    jne .L5 

Như trình biên dịch không phải lo lắng về chủ đề (v1 không phải là nguyên tử) . Nó không thể chỉ giả định không có gì khác sẽ được nhìn vào giá trị này và đi trước và giữ nó trong một đăng ký trong khi vòng lặp được quay?

Xem https://godbolt.org/g/RiF3B4 để lắp ráp đầy đủ và lựa chọn trình biên dịch để lựa chọn!

+0

Ví dụ bạn đã liên kết gặp sự cố thêm. 'v1' được uninitialised khi lần đầu tiên được sử dụng. Đây là UB và sẽ gây gcc và clang để làm những điều kỳ quặc tại thời gian tối ưu hóa. –

+0

Ồ vâng - điểm công bằng - đó không phải là mã thực mặc dù tôi chỉ muốn hiển thị vấn đề tôi quan tâm và điểm bắt đầu cho v1 không thực sự quan trọng. Mã thực tế là một chút tinh tế hơn. – JCx

+0

đọc thú vị và thận trọng: https://blog.regehr.org/archives/759 –

Trả lời

11

aliasing: v1 là biến thành viên và có thể là x điểm tại đó. Do đó, một trong các ghi cho các phần tử của x có thể thay đổi v1.

Trong C99, bạn có thể sử dụng từ khóa giới hạn trên đối số hàm của kiểu con trỏ để thông báo cho trình biên dịch rằng nó không bí danh bất kỳ điều gì khác nằm trong phạm vi của hàm. Một số trình biên dịch C++ cũng hỗ trợ nó, mặc dù nó không phải là tiêu chuẩn. (Được sao chép từ một trong các nhận xét của tôi.)

+0

Wow - ok - vậy có cách nào để nói điều đó không xảy ra? – JCx

+2

@JCx: Trong C99, bạn có thể sử dụng từ khóa 'limits' trên đối số hàm của kiểu con trỏ để thông báo cho trình biên dịch rằng nó không bí danh bất kỳ thứ gì khác nằm trong phạm vi của hàm. [Một số trình biên dịch C++ cũng hỗ trợ nó] (https://stackoverflow.com/questions/776283/what-does-the-restrict-keyword-mean-in-c#1965502), mặc dù nó không phải là tiêu chuẩn. –

+2

Được rồi - được kiểm tra bằng tiếng kêu. Hạn chế sắp xếp nó ra. Nice :) – JCx

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