C Khuyến mãi số nguyên trên MCU 8 bit


14

Sử dụng avr-gcc làm ví dụ, các kiểu int được chỉ định là rộng 16 bit. Việc thực hiện các hoạt động trên các toán hạng 8 bit trong C dẫn đến các toán hạng đó được chuyển đổi thành các kiểu int 16 bit do quảng cáo số nguyên trong C. Điều này có nghĩa là tất cả các hoạt động số học 8 bit trên một AVR sẽ mất nhiều thời gian hơn nếu được viết bằng C nếu được viết trong hội đồng do khuyến mãi số nguyên của C?


1
Tôi không nghĩ vậy, trình biên dịch sẽ nhận ra rằng biến đích là một char (không dấu), do đó nó sẽ không bận tâm trong việc tính toán 8 bit hàng đầu. Tuy nhiên, tôi thấy rằng GCC đôi khi không tốt trong việc tối ưu hóa mã, do đó nếu bạn viết mã trong ASM, kết quả MGIHT sẽ nhanh hơn. Tuy nhiên, trừ khi bạn đang thực hiện các nhiệm vụ / interrutps rất quan trọng về thời gian, với ràng buộc ngân sách rất mạnh, thì bạn nên chọn bộ xử lý mạnh hơn và lập trình nó trong C, hoặc đừng lo lắng về hiệu suất thấp hơn (thay vào đó hãy xem xét thời gian -to-market, khả năng đọc / tái sử dụng mã tốt hơn, ít lỗi hơn, ecc ..).
hack tiếp theo

Tôi xin lỗi vì không có thời gian để kiểm tra. Tuy nhiên, tôi nghĩ rằng có một cờ dòng lệnh cho gcc sẽ điều khiển 'quảng cáo số nguyên'. Thậm chí có thể có một pragma để kiểm soát nó cho các đoạn mã cụ thể. Làm thế nào quan trọng là hiệu suất? Trong rất nhiều ứng dụng của một AVR, sự khác biệt về tốc độ đối với một số số học không phải là vấn đề. Foxus về việc làm cho mã làm việc chính xác đầu tiên. Sau đó, nếu có một vấn đề hiệu suất, tìm hiểu nó là gì. Sẽ thật dễ dàng để lãng phí thời gian mã hóa trong trình biên dịch chương trình, chỉ có bạn thấy nó không thành vấn đề.
xe cứu thương

1
chỉ cần tháo rời và xem trình biên dịch đang làm gì. Từ quan điểm ngôn ngữ thuần túy có. việc thực hiện ở đây là không điển hình. thông thường int cố gắng tự căn chỉnh với kích thước thanh ghi và nếu bạn có các thanh ghi 16 bit thì toán 8 bit thực sự rẻ hơn ở 16 bit so với 8. Nhưng đây là cách khác và với mcu 8 bit, sẽ rất hợp lý khi thực hiện int 16 bit. Vì vậy, có lẽ bạn nên sử dụng uchar khi bạn quan tâm đến vấn đề này nhưng đừng biến nó thành thói quen lập trình phổ biến vì nó làm tổn thương bạn ở mọi nơi khác.
old_timer

3
Ghi nhớ: Tránh trả lời các câu hỏi trong bình luận.
ống

4
Những loại câu hỏi này tốt hơn để hỏi các chuyên gia C tại SO, vì đây là một câu hỏi phần mềm thuần túy. Quảng cáo số nguyên trong C là một chủ đề hơi phức tạp - lập trình viên C trung bình sẽ có rất nhiều quan niệm sai lầm về nó.
Lundin

Câu trả lời:


16

Mẩu chuyện dài:

Việc khuyến mãi số nguyên lên 16 bit luôn diễn ra - tiêu chuẩn C thực thi điều này. Nhưng trình biên dịch được phép tối ưu hóa tính toán xuống còn 8 bit (trình biên dịch hệ thống nhúng thường khá tốt ở các tối ưu hóa như vậy), nếu có thể suy ra rằng dấu hiệu sẽ giống như đã xảy ra nếu loại được quảng cáo.

Đây không phải là luôn luôn như vậy! Thay đổi chữ ký ngầm gây ra bởi quảng cáo số nguyên là một nguồn lỗi phổ biến trong các hệ thống nhúng.

Giải thích chi tiết có thể được tìm thấy ở đây: Quy tắc khuyến mãi kiểu ngầm định .


8
unsigned int fun1 ( unsigned int a, unsigned int b )
{
    return(a+b);
}

unsigned char fun2 ( unsigned int a, unsigned int b )
{
    return(a+b);
}

unsigned int fun3 ( unsigned char a, unsigned char b )
{
    return(a+b);
}

unsigned char fun4 ( unsigned char a, unsigned char b )
{
    return(a+b);
}

như mong đợi fun1 là tất cả các ints, toán 16 bit cũng vậy

00000000 <fun1>:
   0:   86 0f           add r24, r22
   2:   97 1f           adc r25, r23
   4:   08 95           ret

Mặc dù về mặt kỹ thuật không chính xác vì đây là một bổ sung 16 bit được gọi ra bởi mã, ngay cả trình biên dịch không được tối ưu hóa này đã loại bỏ adc do kích thước kết quả.

00000006 <fun2>:
   6:   86 0f           add r24, r22
   8:   08 95           ret

Không thực sự ngạc nhiên ở đây chương trình khuyến mãi xảy ra, các trình biên dịch đã không sử dụng để làm điều này không chắc chắn phiên bản nào đã bắt đầu xảy ra, đã chạy vào đầu sự nghiệp của tôi và mặc dù các trình biên dịch quảng bá không theo thứ tự (giống như ở trên), thực hiện quảng cáo mặc dù tôi bảo nó làm toán uchar, không ngạc nhiên.

0000000a <fun3>:
   a:   70 e0           ldi r23, 0x00   ; 0
   c:   26 2f           mov r18, r22
   e:   37 2f           mov r19, r23
  10:   28 0f           add r18, r24
  12:   31 1d           adc r19, r1
  14:   82 2f           mov r24, r18
  16:   93 2f           mov r25, r19
  18:   08 95           ret

và lý tưởng, tôi biết nó là 8 bit, muốn có kết quả 8 bit nên tôi chỉ đơn giản bảo nó làm 8 bit cho đến hết.

0000001a <fun4>:
  1a:   86 0f           add r24, r22
  1c:   08 95           ret

Vì vậy, nói chung, tốt hơn là nhắm vào kích thước thanh ghi, lý tưởng nhất là kích thước của một (u) int, đối với một mcu 8 bit như thế này, các tác giả trình biên dịch đã phải thỏa hiệp ... Điểm không được tạo thành thói quen sử dụng uchar cho toán học mà bạn biết không cần nhiều hơn 8 bit như khi bạn di chuyển mã đó hoặc viết mã mới như thế trên bộ xử lý với các thanh ghi lớn hơn bây giờ trình biên dịch phải bắt đầu che dấu và mở rộng, một số thực hiện theo một số hướng dẫn, và những người khác thì không.

00000000 <fun1>:
   0:   e0800001    add r0, r0, r1
   4:   e12fff1e    bx  lr

00000008 <fun2>:
   8:   e0800001    add r0, r0, r1
   c:   e20000ff    and r0, r0, #255    ; 0xff
  10:   e12fff1e    bx  lr

buộc 8 bit chi phí nhiều hơn. Tôi đã lừa dối một chút / rất nhiều, sẽ cần những ví dụ phức tạp hơn một chút để thấy nhiều hơn về điều này một cách công bằng.

EDIT dựa trên thảo luận ý kiến

unsigned int fun ( unsigned char a, unsigned char b )
{
    unsigned int c;
    c = (a<<8)|b;
    return(c);
}

00000000 <fun>:
   0:   70 e0           ldi r23, 0x00   ; 0
   2:   26 2f           mov r18, r22
   4:   37 2f           mov r19, r23
   6:   38 2b           or  r19, r24
   8:   82 2f           mov r24, r18
   a:   93 2f           mov r25, r19
   c:   08 95           ret

00000000 <fun>:
   0:   e1810400    orr r0, r1, r0, lsl #8
   4:   e12fff1e    bx  lr

Không bất ngờ. Mặc dù tại sao trình tối ưu hóa lại bỏ hướng dẫn thêm đó, bạn không thể sử dụng ldi trên r19? (Tôi biết câu trả lời khi tôi hỏi nó).

EDIT2

cho avr

avr-gcc --version
avr-gcc (GCC) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

để tránh thói quen xấu hay không so sánh 8 bit

arm-none-eabi-gcc --version
arm-none-eabi-gcc (GCC) 7.2.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

tối ưu hóa rõ ràng chỉ mất một giây để thử với trình biên dịch của riêng bạn để xem nó so sánh với đầu ra của tôi như thế nào, nhưng dù sao:

whatever-gcc -O2 -c so.c -o so.o
whatever-objdump -D so.o

Và có, sử dụng byte cho các biến có kích thước byte, chắc chắn trên avr, pic, v.v., sẽ giúp bạn tiết kiệm bộ nhớ và bạn muốn thực sự cố gắng bảo tồn nó ... nếu bạn thực sự đang sử dụng nó, nhưng như thể hiện ở đây càng ít càng tốt sẽ có trong bộ nhớ, càng nhiều trong các thanh ghi càng tốt, vì vậy tiết kiệm flash xuất hiện bằng cách không có thêm biến, tiết kiệm ram có thể hoặc không thể là thật ..


2
"Các trình biên dịch đã không sử dụng để làm điều này không chắc chắn phiên bản nào đã bắt đầu xảy ra, đã bắt đầu sớm trong sự nghiệp của tôi và mặc dù các trình biên dịch quảng bá không theo thứ tự (giống như ở trên), thực hiện quảng cáo mặc dù tôi đã bảo nó làm toán uchar, không ngạc nhiên." Đó là vì hệ thống nhúng trình biên dịch C đã từng có kinh khủng phù hợp tiêu chuẩn :) Trình biên dịch thường được phép tối ưu hóa, nhưng ở đây nó không thể khấu trừ rằng kết quả sẽ phù hợp trong một unsigned charnên nó để thực hiện các chương trình khuyến mãi đến 16 bit, theo yêu cầu theo tiêu chuẩn.
Lundin

1
@old_timer (a<<8)|bluôn sai đối với bất kỳ hệ thống nào intcó 16 bit. asẽ được quảng bá ngầm intđược ký kết. Trong trường hợp agiữ một giá trị trong MSB, cuối cùng bạn chuyển dữ liệu đó thành bit dấu của số 16 bit, gọi hành vi không xác định.
Lundin

1
fun3 rất vui..ny ... hoàn toàn không được tối ưu hóa bởi trình biên dịch ... Được coi là r1 luôn là 0 trong GCC và chỉ ra ra, rb, {rh, rl} các thanh ghi cho các biến a, b và kết quả, trình biên dịch có thể đã thực hiện: 1) Mov rh, r1; 2) Mov rl, ra; 2) thêm rl, rb; 3) adc rh, rh; 4) 4 Hướng dẫn, so với 7 hoặc 8 ... Hướng dẫn 1 có thể được thay đổi trong ldi rh, 0.
hack tiếp theo

1
Đây sẽ là một câu trả lời tốt hơn nếu nó chỉ định trình biên dịch và các tùy chọn có liên quan đang sử dụng.
Russell Borogove

1
Đó là một ý tưởng tốt để tránh sử dụng int / char, v.v. và thay vào đó sử dụng int16_t và int8_t rõ ràng và dễ đọc hơn nhiều.
người dùng

7

Không nhất thiết, vì trình biên dịch hiện đại làm tốt công việc tối ưu hóa mã được tạo. Ví dụ: nếu bạn viết z = x + y;vị trí của tất cả các biến unsigned char, trình biên dịch được yêu cầu quảng bá chúng unsigned inttrước khi thực hiện các phép tính. Tuy nhiên, vì kết quả cuối cùng sẽ hoàn toàn giống nhau mà không cần quảng cáo, trình biên dịch sẽ tạo mã chỉ thêm các biến 8 bit.

Tất nhiên, điều này không phải lúc nào cũng đúng, ví dụ kết quả z = (x + y)/2;sẽ phụ thuộc vào byte trên, vì vậy việc quảng cáo sẽ diễn ra. Nó vẫn có thể tránh được mà không cần dùng đến lắp ráp bằng cách đưa kết quả trung gian trở lại unsigned char.

Một số sự thiếu hiệu quả như vậy có thể tránh được bằng cách sử dụng các tùy chọn trình biên dịch. Ví dụ, nhiều trình biên dịch 8 bit có pragma hoặc chuyển đổi dòng lệnh để phù hợp với các kiểu liệt kê trong 1 byte, thay vì inttheo yêu cầu của C.


4
"Trình biên dịch là cần thiết để thúc đẩy chúng thành int unsign". Không, trình biên dịch được yêu cầu để quảng bá chúng int, vì charrất có thể sẽ không có cùng thứ hạng chuyển đổi như inttrên bất kỳ nền tảng nào.
Lundin

3
"Ví dụ: nhiều trình biên dịch 8 bit có pragma hoặc chuyển đổi dòng lệnh để phù hợp với các kiểu liệt kê trong 1 byte, thay vì int theo yêu cầu của C." Tiêu chuẩn C cho phép các biến liệt kê được phân bổ trong 1 byte. Nó chỉ yêu cầu các hằng số liệt kê phải int(có nó không nhất quán). C11 6.7.2.2Each enumerated type shall be compatible with char, a signed integer type, or an unsigned integer type. The choice of type is implementation-defined...
Lundin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.