2016-04-15 14 views
7

Cho một tệp nhị phân với các trường nhỏ 32 bit mà tôi cần phân tích cú pháp, tôi muốn viết mã phân tích biên dịch chính xác độc lập với tính cuối của máy thực thi mã đó. Hiện tại tôi sử dụngChuyển đổi tối ưu và di động của endian trong c/C++

uint32_t fromLittleEndian(const char* data){ 
    return uint32_t(data[3]) << (CHAR_BIT*3) | 
     uint32_t(data[2]) << (CHAR_BIT*2) | 
     uint32_t(data[1]) << CHAR_BIT | 
     data[0]; 
} 

điều này, tuy nhiên tạo ra lắp ráp không tối ưu. Trên máy tính của tôi g++ -O3 -S sản xuất:

_Z16fromLittleEndianPKc: 
.LFB4: 
    .cfi_startproc 
    movsbl 3(%rdi), %eax 
    sall $24, %eax 
    movl %eax, %edx 
    movsbl 2(%rdi), %eax 
    sall $16, %eax 
    orl %edx, %eax 
    movsbl (%rdi), %edx 
    orl %edx, %eax 
    movsbl 1(%rdi), %edx 
    sall $8, %edx 
    orl %edx, %eax 
    ret 
    .cfi_endproc 

lý do tại sao điều này xảy ra? Làm thế nào tôi có thể thuyết phục nó để sản xuất mã tối ưu khi biên soạn trên máy little endian:

_Z17fromLittleEndian2PKc: 
.LFB5: 
    .cfi_startproc 
    movl (%rdi), %eax 
    ret 
    .cfi_endproc 

mà tôi đã nhận được bằng cách biên dịch:

uint32_t fromLittleEndian2(const char* data){ 
    return *reinterpret_cast<const uint32_t*>(data); 
} 

Kể từ khi tôi biết máy của tôi rất ít về cuối nhỏ, tôi biết rằng lắp ráp ở trên là tối ưu, nhưng nó sẽ thất bại nếu biên dịch trên máy tính lớn. Nó cũng vi phạm các quy tắc bí danh nghiêm ngặt, vì vậy nếu được inline, nó có thể tạo ra UB ngay cả trên các máy cuối nhỏ. Có một mã hợp lệ sẽ được biên dịch để tối ưu hóa lắp ráp nếu có thể?

Vì tôi mong đợi chức năng của mình được gạch chân rất nhiều, bất kỳ loại phát hiện cuối kỳ thời gian chạy nào đều không có trong câu hỏi. Cách thay thế duy nhất để viết mã C/C++ tối ưu là sử dụng phát hiện thời gian biên dịch cuối cùng, và sử dụng template s hoặc #define s để quay trở lại mã không hiệu quả nếu mục tiêu cuối cùng không phải là nhỏ. Điều này tuy nhiên có vẻ là khá khó khăn để được thực hiện portably.

+0

Bạn không thể khớp 'reinterpret_cast'. Nó không làm bất kỳ sắp xếp lại byte nào. Nếu bạn phải nhảy các byte ngẫu nhiên, bạn phải trả tiền cho ban nhạc. – user4581301

+0

Vấn đề là nếu nền tảng biên dịch mục tiêu của tôi là ít endian thì tôi không cần byte shuffle - trình biên dịch cũng nên biết điều đó, nhưng nó tạo ra mã xáo trộn byte anyway. –

+0

Thing là trình biên dịch không biết bạn đang lật endian. Nó chỉ thấy một loạt các thay đổi và ors. Tuy nhiên, sẽ là một mẹo hay. Có thể chơi ở mức makefile và biên dịch và liên kết trong các chức năng chính xác, nhưng điều đó sẽ giết bất kỳ nội tuyến. – user4581301

Trả lời

1

Các thư viện nền tảng khác nhau mà tôi biết làm điều này bằng cách # xác định macro cho các thói quen hoán đổi cuối cùng dựa trên giá trị của #define BIG_ENDIAN. Trong trường hợp endianness nguồn phù hợp endianness mục tiêu của bạn, bạn có thể chỉ:

#ifdef LITTLE_ENDIAN 
    #define fromLittleEndian(x) (x) 
#else 
    #define fromLittleEndian(x) _actuallySwapLittle((x)) 
#endif 

Ví dụ:

http://man7.org/linux/man-pages/man3/endian.3.html

http://fxr.watson.org/fxr/source/sys/endian.h

+0

' 'dường như không mang tính di động. Xem https://gcc.gnu.org/ml/gcc-help/2007-07/msg00342.html –

+0

Bạn phải chọn ở đây - tối ưu hoặc di động. @j_kubik có phiên bản di động không tối ưu. Nhiều câu trả lời khác sẽ đề xuất các kỹ thuật khác có thể di chuyển nhiều hơn hoặc ít hơn, nhưng cách duy nhất để đảm bảo rằng bạn nhận được đầu ra không có gì trong trường hợp không làm gì là sử dụng bộ tiền xử lý. Không có gì đảm bảo rằng bất kỳ trình biên dịch C++ đã cho nào cũng sẽ nhận ra trường hợp không-làm gì khác. –

+0

Tôi đoán câu trả lời là cố gắng phát hiện nền tảng đích và nếu không thành công, thì hãy sử dụng mã tối ưu dưới dạng thu gọn. –

2

ngắn câu trả lời - sử dụng htonl - gonna của nó được tối ưu hóa lên wazzoo

+2

Vấn đề duy nhất là thứ tự mạng là lớn endian. –

+0

yup và htonl sẽ biết điều đó và chuyển đổi hay không phụ thuộc vào máy đang chạy trên – pm100

+0

Tôi biết điều đó, nhưng 'htonl' và bạn bè luôn chuyển đổi từ/sang máy cuối đến/từ lớn endian (endian mạng). Tập tin của tôi là theo định nghĩa little-endian, và tôi cần một bộ fuction chuyển đổi từ/đến máy endian đến/từ nhỏ endian. Không có cách nào tôi nhìn thấy tôi có thể sử dụng 'htonl' hoặc' ntohl' để giải quyết vấn đề của tôi, ngoại trừ có lẽ để chuyển đổi luôn luôn thành lớn endian và sau đó luôn luôn làm một số shuffling byte anyway. Điều này không có khả năng ở mức tối ưu. –

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