簡體   English   中英

使用 uint64_t 的不便

[英]Inconveniences of using uint64_t

我有一個高度可移植的庫(即使沒有內核,它也可以在任何地方編譯和運行良好),我希望它盡可能保持可移植性。 到目前為止,我已經避免使用 64 位數據類型,但我現在可能需要使用它們——准確地說,我需要一個 64 位位掩碼。

我從來沒有真正考慮過,我還不夠硬件專家(尤其是在嵌入式系統方面),但我現在想知道:使用uint64_t (或等效地, uint_least64_t )有哪些不便之uint_least64_t 我可以想到兩種方法來解決我的問題:

  1. 實際可移植性:是否所有微控制器(包括 8 位 CPU)都能夠處理 64 位整數?
  2. 性能:與 32 位整數相比,8 位 CPU 對 64 位整數執行按位運算的速度有多慢? 我正在設計的函數將只有一個 64 位變量,但會對其執行大量按位運算(即在循環中)。

對符合標准的 C 編譯器有各種最低要求。 C 語言允許兩種形式的編譯器:托管獨立 Hosted 旨在運行在 OS 之上,而獨立運行則無需 OS。 大多數嵌入式系統編譯器都是獨立的實現。

獨立編譯器有一些回旋余地,它們不需要支持所有標准庫,但需要支持它們的最小子集。 這包括stdint.h (參見 C17 4/6)。 這反過來又要求編譯器實現以下內容(C17 7.20.1.2/3):

需要以下類型:

int_least8_t int_least16_t int_least32_t int_least64_t
uint_least8_t uint_least16_t uint_least32_t uint_least64_t

所以微控制器編譯器不需要支持uint64_t ,但它必須(奇怪的是)支持uint_least64_t 實際上,這意味着編譯器也可以添加uint64_t支持,因為在這種情況下它是相同的。

至於 8 位 MCU 支持什么……它通過指令集支持 8 位算術,在某些特殊情況下還支持一些使用索引寄存器的 16 位運算。 但一般來說,每當使用大於 8 位的類型時,它都必須依賴軟件庫。

所以如果你在 8bit 上嘗試 32 位算術,它會將一些編譯器軟件庫與代碼內聯,結果將是數百條匯編指令,使得這些代碼非常低效和消耗內存。 64位會更糟。

與缺乏 FPU 的 MCU 上的浮點數相同,這些也會通過軟件浮點庫生成極其低效的代碼。


為了說明這一點,請看一下在 8 位 AVR (gcc) 上進行一些非常簡單的 64 位加法的未優化代碼: https : //godbolt.org/z/ezbKjY
它實際上支持uint64_t但編譯器產生了大量的開銷代碼,大約 100 條指令。 在它中間,調用了隱藏在可執行文件中的內部編譯器函數call __adddi3

如果我們啟用優化,我們會得到

add64:
        push r10
        push r11
        push r12
        push r13
        push r14
        push r15
        push r16
        push r17
        call __adddi3
        pop r17
        pop r16
        pop r15
        pop r14
        pop r13
        pop r12
        pop r11
        pop r10
        ret

我們將不得不深入挖掘庫源或單步執行程序集,以查看__adddi3有多少代碼。 我猜它仍然不是一個微不足道的功能。

因此,正如您所希望的那樣,在 8 位 CPU 上執行 64 位算術是一個非常糟糕的主意。

好吧,如果您主要關心的是保持相當水平的兼容性,這就是避免使用 64 位數字的原因,為什么不使用int整數數組,並考慮使用一個完整的整數來存儲,比如說,30位。

我建議您查看有關使用位掩碼(大於 32 位)來表示例如select(2)系統調用所接觸的文件以及如何使用FDSET宏的標准庫源。

事實是,您可能會遇到問題,即決定是在用於表示位圖的數據類型中跨越 32 位的限制,還是通過使用仍然可用的 64 位類型(暫時)解決該問題。 當您獲得 64 位位掩碼時,這將是下一個規模問題,並且您最終將不得不越界。

作為練習,您現在可以這樣做,您將了解到最后的數據類型或多或少是一組大的位,您可以隨意使用它們。 您是否打算使用 80 位long double精度值來存儲大於 64 位的位掩碼? 我認為你不會,所以想想陣列解決方案,這可能會一勞永逸地解決你的問題。

如果你的問題是我的情況,我會寫一個 32 位無符號數的數組,所以所有位在移位、位操作等方面的行為都是一樣的。


#define FDSET_TYPE(name, N)  unsigned int name[((N) + 31U) >> 5]
#define FDSET_ISSET(name, N) ((name[(N) >> 5] & 1 << (N & 0x1f)) != 0)

...

    FDSET_TYPE(name, 126);

...

    if (FDSET_ISSET(name, 35)) { ...

在上面的示例中, FDSET_TYPE宏允許您聲明作為第二個參數傳遞的位數的變量,並使用無符號 32 位整數數組實現它,四舍五入到下一個值以允許包含所有位. FDSET_ISSET(name, 35)計算單元格和請求位所在的偏移量,並用您傳遞的數字除以 32 的余數對其進行掩碼 --- 但是當我們選擇 2 的冪時,y 使用掩碼0x1f屏蔽數字的最后 5 位以獲得余數 mod 32)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM