2012-12-15 33 views
12

này là một phần của tiêu đề SDK CryENGINE làm tôi chú ý:Mặt nạ nhánh hoạt động như thế nào trong CryENGINE 3?

branchmask.h

#ifndef __BRANCHLESS_MASK__ 
#define __BRANCHLESS_MASK__ 

/////////////////////////////////////////// 
// helper functions for branch elimination 
// 
// msb/lsb - most/less significant byte 
// 
// mask - 0xFFFFFFFF 
// nz - not zero 
// zr - is zero 

ILINE const uint32 nz2msb(const uint32 x) 
{ 
    return -(int32)x | x; 
} 

ILINE const uint32 msb2mask(const uint32 x) 
{ 
    return (int32)(x) >> 31; 
} 

ILINE const uint32 nz2one(const uint32 x) 
{ 
    return nz2msb(x) >> 31; // int((bool)x); 
} 

ILINE const uint32 nz2mask(const uint32 x) 
{ 
    return (int32)msb2mask(nz2msb(x)); // -(int32)(bool)x; 
} 


ILINE const uint32 iselmask(const uint32 mask, uint32 x, const uint32 y)// select integer with mask (0xFFFFFFFF or 0x0 only!!!) 
{ 
    return (x & mask) | (y & ~mask); 
} 


ILINE const uint32 mask_nz_nz(const uint32 x, const uint32 y)// mask if(x != 0 && y != 0) 
{ 
    return msb2mask(nz2msb(x) & nz2msb(y)); 
} 

ILINE const uint32 mask_nz_zr(const uint32 x, const uint32 y)// mask if(x != 0 && y == 0) 
{ 
    return msb2mask(nz2msb(x) & ~nz2msb(y)); 
} 


ILINE const uint32 mask_zr_zr(const uint32 x, const uint32 y)// mask if(x == 0 && y == 0) 
{ 
    return ~nz2mask(x | y); 
} 

#endif//__BRANCHLESS_MASK__ 

Có thể ai đó ném một giải thích ngắn gọn như thế nào chính xác được các chức năng này dự định sẽ được sử dụng để làm giảm các chi nhánh? ILINE Tôi giả sử là nội lực được xác định trước hoặc một cái gì đó như thế. Tôi đã tìm kiếm trên Google về nó, nhưng tất cả những gì tôi tìm thấy là các bản sao của các tiêu đề CryENGINE được tải lên ở các trang khác nhau, nhưng không có thảo luận về vấn đề cụ thể này.

+0

với dự đoán nhánh tốt hơn tôi đoán – user1849534

+0

@didierc Hay đúng hơn, các ví dụ này loại bỏ hoàn toàn các nhánh. – Mysticial

Trả lời

12

Các hàm này trả về các mặt nạ bit có thể có và kết quả trong các phép tính khác, để thực hiện các thao tác không có điều kiện và do đó không giới thiệu chi nhánh.

Ví dụ:

  • nz2mask lợi nhuận 0 nếu đối số là 0, và 0xffffffff khác.
  • msb2mask trả lại 0 nếu bit trên cùng của đối số là 00xffffffff nếu đó là 1.

Vì vậy, nếu bạn có mã tương tự (với lệnh x86 để tham khảo):

if(a != 0) x += y; 
    // test  ebx,ebx 
    // je   skip 
    // add   dword ptr [x],eax 
    // skip: 

Bạn có thể thay thế nó bằng:

x += y & (nz2mask(a)); 
    // mov  ecx,ebx 
    // neg  ecx 
    // or  ecx,ebx 
    // sar  ecx,1Fh 
    // and  ecx,eax 
    // add  ecx,dword ptr [x] 

Nó tạo ra nhiều hướng dẫn (ít nhất là trên x86) , nhưng nó tránh một nhánh.

Sau đó, có chức năng bổ sung như iselmask() cho phép lựa chọn một trong hai đầu vào dựa trên mặt nạ cung cấp, vì vậy bạn có thể thay thế:

x = (a != 0) ? r1 : r2; 

với

x = iselmask(nz2mask(a), r1, r2); 

Một lần nữa, các chức năng này nên nội tuyến và biên dịch thành bộ lắp ráp tương đối hiệu quả, giao dịch một chút các phép toán bổ sung để không phân nhánh.

+1

được thăng hạng. Trong ví dụ đầu tiên, chúng ta có thể thấy rằng có một bài kiểm tra (lệnh 'if'), ở cấp mã máy được dịch sang nhánh có điều kiện. Trong hướng dẫn thay thế, không tìm thấy thêm kiểm tra nào và mã máy sẽ không chứa nhánh. – didierc

+0

cảm ơn cho câu trả lời, tôi đoán tôi sẽ thay thế nếu elses của tôi với điều này ngay bây giờ: D –

+0

Chắc chắn hồ sơ kết quả trước khi mù quáng làm loại công cụ này - nó không phải luôn luôn là một chiến thắng. Bạn phải biết rằng phân nhánh đang làm bạn mất hiệu suất trước khi cố gắng loại bỏ nó. – JasonD

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