跳轉到內容

Ada 程式設計/錯誤

來自華夏公益教科書,開放的書籍,開放的世界

Ada. Time-tested, safe and secure.
Ada。經久耐用、安全可靠。


一些語言特性經常被誤解,導致常見的程式設計錯誤、效能下降和可移植性問題。以下這些 Ada 語言的錯誤用法,經常出現在 Ada 初學者編寫的程式碼中。

pragma Atomic & Volatile

[編輯 | 編輯原始碼]

使用 原子易變 變數進行 任務處理 幾乎總是錯誤的。[1] 當一個物件是原子的,它僅僅意味著它將以原子方式從記憶體中讀取或寫入。編譯器不會在訪問該物件時生成原子指令或記憶體屏障,它只會

  • 檢查體系結構是否保證原子記憶體載入和儲存,
  • 禁止一些編譯器最佳化,例如對物件的重新排序或抑制冗餘訪問。

例如,以下程式碼,其中A是一個原子物件,可能會被誤解

A := A + 1;  -- Not an atomic increment!

編譯器不會(並且根據標準不允許)生成原子增量指令來直接從記憶體中增量並更新變數A[2] 這是編譯器生成的程式碼

  A := A + 1;
804969f:	a1 04 95 05 08       	mov    0x8059504,%eax
80496a4:	40                   	inc    %eax
80496a5:	a3 04 95 05 08       	mov    %eax,0x8059504

可以看到,不會生成任何原子增量指令或測試並設定操作碼。與其他程式語言一樣,如果程式中需要這些特定指令,則必須使用機器程式碼插入顯式編寫。[3]

上面的程式碼片段等同於以下程式碼(兩個程式碼序列生成完全相同的目的碼),其中T是一個(非原子)臨時變數

T := A;      -- A is copied atomically to local variable T
T := T + 1;  -- local variable T is incremented
A := T;      -- A is stored atomically

因此,從多個任務同時修改原子變數是錯誤的。例如,兩個任務並行遞增一個計數器。即使在單處理器上,也應該使用其他 Ada 任務處理特性,例如保護物件。在多處理器中,根據記憶體一致性模型,使用各種原子或易變變數進行任務通訊可能會產生意想不到的結果。[2][4] 因此,在使用原子物件進行任務資料共享或同步時,尤其是在多處理器中,應格外小心。

參考文獻

[編輯 | 編輯原始碼]
  1. Arch Robison (2007-11-30). "易變:對於多執行緒程式設計幾乎無用". Intel 軟體網路. 檢索於 2008-05-30. 有一種普遍的觀點認為,關鍵字易變對於多執行緒程式設計很有用 (...) 易變對於多執行緒程式設計幾乎無用。
  2. a b Ian Lance Taylor (2008-03-05). "易變". 檢索於 2008-05-28. 使用 [C/C++ 限定符] 易變並不意味著變數以原子方式訪問;不使用鎖。使用易變並不意味著多核系統中的其他核心將看到記憶體訪問;不使用快取重新整理。 (...) 使用易變不意味著任何型別的記憶體屏障;處理器可以並且將會重新排列易變記憶體訪問。 (...) 你不應該使用多個此類變數在任何一對執行緒之間進行通訊,因為不能保證不同的執行緒會以相同的順序看到訪問。
  3. Laurent Guerby (1995). "C.5 共享變數控制". Ada 95 Rationale. Intermetrics. 有時需要訪問特定的機器指令 (...)。例如,對共享記憶體執行復合操作的原子指令,例如測試並設定和比較並交換 (...) {{cite book}}: |access-date= requires |url= (幫助); External link in |chapter= (幫助); Unknown parameter |month= ignored (幫助)
  4. Sarita V. Adve, Kourosh Gharachorloo (1996). "共享記憶體一致性模型:教程" (PDF). IEEE Computer. 29 (12): 66–76. 檢索於 2008-05-28. {{cite journal}}: Unknown parameter |month= ignored (幫助)

pragma Pack

[編輯 | 編輯原始碼]

精確的資料表示

[編輯 | 編輯原始碼]

重要的是要認識到,pragma Pack 不應該用來指定資料型別的精確表示,而是用來幫助編譯器提高生成程式碼的效率。[1] 編譯器可以自由地忽略該 pragma,因此,如果需要型別的特定表示,則應該使用表示子句(記錄表示子句,和/或屬性 'Size'Component_Size)。

按位操作

[編輯 | 編輯原始碼]

儘管在 Ada 83 中,打包布林陣列被用來進行按位操作,[2] 但是從 Ada 95 開始,模型別 更適合這些操作。 [3] 可以權衡使用命名布林陣列索引的優勢,例如Traffic_Lights'(Red => True,others => False), 取決於用例。

'Bit_Order 屬性

[edit | edit source]

'Bit_Order 屬性並非旨在將資料在大小端機器之間進行轉換(它影響的是位編號,而不是位元組順序)。當指定非本機位順序時,編譯器不會生成程式碼來重新排序多位元組欄位。[4][5][6]

參考文獻

[edit | edit source]
  1. Adam Beneschan (2008-01-09). "Pragma Pack vs. Convention C, portability issue?". comp.lang.ada. (Web link). Retrieved on 2008-05-27.
  2. 軟體生產力聯盟 (1995 年 10 月)。Ada 95 質量和風格指南,"10.5.7 打包布林陣列移位"
  3. 軟體生產力聯盟 (1995 年 10 月)。Ada 95 質量和風格指南,"10.6.3 模組型別上的位操作"
  4. AI95-00133-01 (1996-05-07). "控制位排序". Class: binding interpretation. Ada Rapporteur Group. Bit_Order 子句與位的編號有關,與資料翻轉互操作性無關。
  5. ISO/IEC 8652:2007. "13.5.3 位排序 (9/2)". Ada 2005 參考手冊. Retrieved 2008-06-02. Bit_Order 子句使編寫可以在具有不同位排序的機器之間移植的 record_representation_clauses 成為可能。它們不保證在這些機器之間透明地交換資料。 {{cite book}}: Unknown parameter |chapterurl= ignored (|chapter-url= suggested) (help)
  6. Thomas Quinot (2013). "Gem #140: Bridging the Endianness Gap". AdaCore. Retrieved 2013-01-31. 構成機器標量的位元組寫入記憶體的順序不會被 Bit_Order 屬性更改——只有機器標量中位的索引會更改。 {{cite web}}: Unknown parameter |month= ignored (help)


'Size 屬性

[edit | edit source]

Ada 程式設計中的一個常見錯誤是假設指定型別 T 的 'Size 會強制編譯器為該型別物件的分配正好是這個數量的位。這不是真的。指定的 T'Size 將強制編譯器在打包陣列和記錄以及 Unchecked_Conversion 中使用此大小,但編譯器仍然可以自由地為獨立物件分配更多位。

對物件本身使用 'Size 來強制物件為指定的值。

另請參閱

[edit | edit source]

華夏公益教科書

[edit | edit source]

參考文獻

[edit | edit source]


華夏公益教科書