Tại sao các loại số nguyên nhanh lại nhanh hơn các loại số nguyên khác?


107

Trong ISO / IEC 9899: 2018 (C18), nó được nêu trong 7.20.1.3:

7.20.1.3 Các loại số nguyên có chiều rộng tối thiểu nhanh nhất

1 Mỗi loại sau đây chỉ định một loại số nguyên thường nhanh nhất 268) để hoạt động với tất cả các loại số nguyên có ít nhất chiều rộng đã chỉ định.

2 Tên typedef int_fastN_tchỉ định loại số nguyên được ký nhanh nhất với chiều rộng ít nhất là N. Tên typedef uint_fastN_tchỉ định loại số nguyên không dấu nhanh nhất có chiều rộng ít nhất là N.

3 Các loại sau là bắt buộc:

int_fast8_t, int_fast16_t, int_fast32_t, int_fast64_t, uint_fast8_t, uint_fast16_t, uint_fast32_t,uint_fast64_t

Tất cả các loại khác của hình thức này là tùy chọn.


268) Loại được chỉ định không được đảm bảo là nhanh nhất cho mọi mục đích; nếu việc triển khai không có căn cứ rõ ràng để chọn một loại so với loại khác, thì nó sẽ chỉ chọn một số loại số nguyên thỏa mãn các yêu cầu về độ ký và độ rộng.


Nhưng không nói rõ tại sao các loại số nguyên "nhanh" này nhanh hơn.

  • Tại sao các loại số nguyên nhanh này nhanh hơn các loại số nguyên khác?

Tôi đã gắn thẻ câu hỏi với C ++, vì các loại số nguyên nhanh cũng có sẵn trong C ++ 17 trong tệp tiêu đề của cstdint. Thật không may, trong ISO / IEC 14882: 2017 (C ++ 17) không có phần như vậy về lời giải thích của họ; Tôi đã thực hiện phần đó trong phần khác của câu hỏi.


Thông tin: Trong C, chúng được khai báo trong tệp tiêu đề của stdint.h.


24
Điểm mấu chốt ở đây là các loại số nguyên này không tách rời, các loại nhanh hơn một cách kỳ diệu. Chúng chỉ là bí danh cho bất kỳ loại thông thường hiện có nào là nhanh nhất trên máy đó cho hoạt động đó.
mtraceur

3
Trình biên dịch phát ra các opc hoạt động của CPU để tải, lưu trữ, mặt nạ và sửa đổi các vị trí bộ nhớ và các thanh ghi có kích thước cụ thể; đó là tất cả những gì CPU thấy. Hệ điều hành không có gì để làm với nó. Đó là tất cả các trình biên dịch đang làm, chính xác như thể bạn đã chỉ định typedef cho trước. (Tôi cho rằng một trình biên dịch được phép xử lý bên trong nó một cách khác biệt - có lẽ hiệu quả hơn, nếu điều đó là có thể - hơn là một người dùng typedef, miễn là không có sự khác biệt rõ ràng nào về hành vi.)
Peter - Tái lập lại

1
@ RobertS-ReinstateMonica Nói chính xác, những "bí danh" này chỉ là typedeftuyên bố. Vì vậy, thông thường , nó được thực hiện ở cấp thư viện tiêu chuẩn. Tất nhiên, hạn chế các puts tiêu chuẩn C không có thực về những gì họ typedefđến - vì vậy ví dụ như một thực hiện điển hình là làm cho int_fast32_tmột typedefsố inttrên một hệ thống 32-bit, nhưng một trình biên dịch giả có thể ví dụ như thực hiện một __int_fastkiểu nội và hứa sẽ làm một số ưa thích tối ưu hóa để chọn loại máy nhanh nhất trên cơ sở từng trường hợp cho các biến của loại đó, và sau đó thư viện có thể chỉ cần typedefđiều đó.
mtraceur

1
@ RobertS-RebstateMonica Vâng, đúng. Bạn nhận được các chương trình hiệu suất tối đa với các cờ biên dịch cụ thể về kiến ​​trúc, điều này làm cho nhị phân ít di động hơn.
Peter - Phục hồi

1
@ Roberts-ReinstateMonica Nó sẽ là hiệu quả nhất trên nền tảng đó đã được biên soạn cho , không nhất thiết trên .
HABO

Câu trả lời:


152

Hãy tưởng tượng một CPU chỉ thực hiện các hoạt động số học 64 bit. Bây giờ hãy tưởng tượng làm thế nào bạn sẽ thực hiện một bổ sung 8 bit không dấu trên CPU như vậy. Nó nhất thiết sẽ liên quan đến nhiều hơn một hoạt động để có được kết quả đúng. Trên CPU như vậy, hoạt động 64 bit nhanh hơn hoạt động trên các độ rộng nguyên khác. Trong tình huống này, tất cả Xint_fastY_tcó thể là bí danh của loại 64 bit.

Nếu CPU hỗ trợ các hoạt động nhanh cho các loại số nguyên hẹp và do đó loại rộng hơn không nhanh hơn loại hẹp hơn, thì đó Xint_fastY_tsẽ không phải là bí danh của loại rộng hơn mức cần thiết để biểu diễn tất cả các bit Y.

Vì tò mò, tôi đã kiểm tra các kích thước trên một triển khai cụ thể (GNU, Linux) trên một số kiến ​​trúc. Chúng không giống nhau trên tất cả các triển khai trên cùng một kiến ​​trúc:

┌────╥───────────────────────────────────────────────────────────┐
 Y     sizeof(Xint_fastY_t) * CHAR_BIT                         
    ╟────────┬─────┬───────┬─────┬────────┬──────┬────────┬─────┤
     x86-64  x86  ARM64  ARM  MIPS64  MIPS  MSP430  AVR 
╞════╬════════╪═════╪═══════╪═════╪════════╪══════╪════════╪═════╡
 8   8       8    8      32   8       8     16      8   
 16  64      32   64     32   64      32    16      16  
 32  64      32   64     32   64      32    32      32  
 64  64      64   64     64   64      64    64      64  
└────╨────────┴─────┴───────┴─────┴────────┴──────┴────────┴─────┘

Lưu ý rằng mặc dù các thao tác trên các loại lớn hơn có thể nhanh hơn, nhưng các loại như vậy cũng chiếm nhiều không gian hơn trong bộ đệm và do đó sử dụng chúng không nhất thiết mang lại hiệu suất tốt hơn. Hơn nữa, người ta không thể luôn tin tưởng rằng việc thực hiện đã đưa ra lựa chọn đúng đắn ngay từ đầu. Như mọi khi, đo lường là cần thiết để có kết quả tối ưu.


Ảnh chụp màn hình của bảng, dành cho người dùng Android:

Ảnh chụp màn hình của bảng trên

(Android không có các ký tự vẽ hình hộp trong phông chữ đơn sắc - ref )


Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Samuel Liew

@RobertSsupportsMonicaCellio số "không giống nhau trên tất cả các kiến ​​trúc" cũng đúng, nhưng ngay lập tức rõ ràng từ dữ liệu được hiển thị, vì vậy tôi sẽ không cần phải nêu rõ ràng. Tôi chỉ hiển thị các giá trị từ một lần thực hiện và thực tế những người khác sẽ có các lựa chọn khác nhau. Kiểm tra ví dụ x86-64 trên windows. Bạn sẽ tìm thấy các kích cỡ khác nhau so với những gì được hiển thị ở đây.
eerorika

@RobertSsupportsMonicaCellio Theo tôi, những bình luận này có liên quan đến câu trả lời, và phù hợp ở đây. Tôi sẽ để một người điều hành di chuyển chúng, nếu họ cảm thấy cần phải làm như vậy.
eerorika

11

Họ không, ít nhất là không đáng tin cậy.

Các loại nhanh chỉ đơn giản là typedefs cho các loại thông thường, tuy nhiên tùy thuộc vào cách thực hiện để xác định chúng. Chúng phải có kích thước tối thiểu được yêu cầu, nhưng chúng có thể lớn hơn.

Đúng là trên một số kiến ​​trúc, một số loại số nguyên có hiệu suất tốt hơn các loại khác. Ví dụ, các triển khai ARM ban đầu có các hướng dẫn truy cập bộ nhớ cho các từ 32 bit và cho các byte không dấu, nhưng chúng không có hướng dẫn cho các từ nửa hoặc byte được ký. Các hướng dẫn nửa chữ và byte đã ký được thêm vào sau đó, nhưng chúng vẫn có các tùy chọn địa chỉ kém linh hoạt hơn, vì chúng phải được đưa vào không gian mã hóa dự phòng. Hơn nữa, tất cả các hướng dẫn xử lý dữ liệu thực tế trên ARM đều hoạt động trên các từ, vì vậy trong một số trường hợp có thể cần phải che đi các giá trị nhỏ hơn sau khi tính toán để cho kết quả chính xác.

Tuy nhiên, cũng có mối quan tâm cạnh tranh của áp lực bộ đệm, ngay cả khi cần nhiều hướng dẫn hơn để tải / lưu trữ / xử lý một giá trị nhỏ hơn. Giá trị nhỏ hơn vẫn có thể hoạt động tốt hơn nếu nó giảm số lần bỏ lỡ bộ đệm.

Các định nghĩa về các loại trên nhiều nền tảng phổ biến dường như chưa được nghĩ đến. Đặc biệt, các nền tảng 64 bit hiện đại có xu hướng hỗ trợ tốt cho các số nguyên 32 bit, tuy nhiên các loại "nhanh" thường không cần thiết 64 bit trên các nền tảng này.

Hơn nữa, các loại trong C trở thành một phần của ABI của nền tảng. Vì vậy, ngay cả khi một nhà cung cấp nền tảng phát hiện ra họ đã đưa ra những lựa chọn ngu ngốc, rất khó để thay đổi những lựa chọn ngu ngốc đó sau này.

Bỏ qua các loại "nhanh". Nếu bạn thực sự lo lắng về hiệu suất số nguyên, hãy điểm chuẩn mã của bạn với tất cả các kích thước có sẵn.


7

Các loại nhanh không nhanh hơn tất cả các loại số nguyên khác - thực tế chúng giống hệt với một số loại số nguyên "thông thường" (chúng chỉ là bí danh cho loại đó) - loại nào xảy ra nhanh nhất để giữ giá trị ít nhất là nhiều bit.

Nó chỉ phụ thuộc vào nền tảng loại số nguyên mỗi loại nhanh là bí danh.

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.