2014-01-06 50 views
5

我最近在Java中發現了「assert」聲明,並且在我調試它時拋棄了我的軟件。我最初的直覺是爲了避免僅僅爲了處理assert語句而製作控制流程語句,但後來我意識到,無論如何,這些控制語句可能會在生產構建過程中被移除,因爲它們的塊將是空的。我的印象是它們將被JIT編譯器消除。圍繞assert語句的迭代器會影響生產構建的性能嗎?

不幸的是,我正在關於JIT編譯器如何工作的模糊回憶,並找不到合適的文檔。我能找到的最好的是IBM的優化過程的brief outline

在我開始實施基於斷言的測試的習慣之前,我想知道是否有最佳實踐來最大限度地降低其對性能的影響。我討厭實施一系列測試,發現它們的集體效應會大幅降低性能,即使它們的影響可以忽略不計。

您能否告訴我以下幾行是否會影響生產版本的性能(默認情況下「禁用斷言」)?

for (T obj : Collection){ 
    assert obj.someProperty(); 
} 

對於獎金,如果我是更復雜的東西,包括僅用於斷言的短期對象,該怎麼辦?

TreeMap<Integer,T> map = new TreeMap<>(); 
int i = 0; 
for (T obj : Collection){ 
    map.put(i,obj); 
    assert obj.someProperty(); 
    i++; 
} 
// assert something about map, then never use it again 

還是一種方法,其唯一的效果是調用「斷言」?

在此先感謝!從「assert」語句的Oracle文檔


相關摘錄:

這些討論如何從類文件,這不正是我擔心刪除斷言。

卸下從類文件程序員斷言的所有痕量 用於資源受限設備開發應用程序可能希望 條斷言出類文件的完全。雖然這使得 無法啓用該領域的斷言,但它也減少了文件大小,可能會導致類加載性能的提高。在缺少高質量JIT的情況下,可能會導致減少佔用面積並提高運行時性能。

斷言工具不提供對剝離類文件外的 斷言的直接支持。 assert語句可能,但是,在與「條件編譯」成語結合使用 在Java語言規範描述 ,使編譯器以消除 的這些所有痕跡從它所生成的類文件聲稱:

static final boolean asserts = ...; //假消除斷言

if(assert)assert;

和常見問題解答部分:

爲什麼不提供一個編譯器標誌完全消除目標文件中的斷言 ?這是一個堅定的要求,有可能在現場啓用斷言,以提高可服務性。它可能允許開發者在編譯時從目標文件中刪除斷言 。斷言可以包含邊 的影響,儘管它們不應該,並且這樣的標誌因此可以以顯着的方式改變程序的行爲。它被認爲是好的 事情只有一個語義與每個有效的Java程序相關聯。此外,我們希望鼓勵用戶在對象 文件中保留斷言,以便可以在該字段中啓用它們。最後,規範要求 這個斷言表現得好像在類被初始化之前運行時一樣運行。如果從類文件中剝離了 斷言,將不可能提供這些語義。但是,請注意,可以使用Java 語言規範中描述的標準「條件編譯慣用語」爲 開發人員實現此效果。

+2

我沒有記得足夠的細節給出一個自信的答案,但我認爲斷言啓用/禁用是在運行時,而不是編譯時。 – Brandon

+0

@布蘭登:謝謝你的提高。如果是這樣的話,我想答案是微不足道的。也許我需要讀一些關於「斷言」的內容。我添加了一個問題鏈接... –

回答

3

您可以從方法的assert中調用,所以我更喜歡的語法是一樣的東西:

private static boolean testSomePropertyTrue(Collection<T> collection) { 
    boolean test = true; 
    for (T obj : collection){ 
     test = test && obj.someProperty(); 
    } 
    return test; 
} 

...

assert testSomePropertyTrue(collection); 

這沒有留下有關代碼歧義被優化掉通過JIT,並且當斷言被啓用時將執行相同的不變檢查。

正如你所寫,你的第二個例子總是會創建TreeMap,不管是否啓用了斷言。在一個函數中包裝整個事物並將其評估爲單個斷言將在運行時完全消除該代碼路徑。

就像這個問題的另一個答案表明,斷言將留下字節碼,無論它們是否啓用,但使用上面顯示的樣式將確定性地阻止這些代碼路徑執行,除非啓用斷言。

+0

謝謝。當你這樣說的時候,看起來很明顯。正如你所說的,我注意到我從第一次收集信息的測試中獲得了一些阻力,然後用assert語句對其進行了測試。有一種情況似乎難以解決 - 我想確認一個更新程序是否做出了預期的改變。這需要在進行更新之前保存以前的狀態,而這實際上是資源密集型步驟。我沒有看到任何方式在仍然更新數據的同時將測試提取到單獨的方法。 –

0

可以在任何你告訴我下面的行是否會拖累了性能上的生產版本(與「斷言」禁用,因爲是默認的)?

你的斷言循環將不論斷言是打開還是關閉運行(即,它們是字節碼的部分,不管 - 當運行代碼斷言啓用)。但是,我不擔心第一個片段的表現。 JIT可能會意識到循環沒有做任何事情,並儘早處理。

爲了獲得獎勵,如果我是更復雜的東西,包括僅用於斷言的短期對象,該怎麼辦?

類似於你的第二個片段的東西很難確定 - 最好的做法可能是自己來定時,儘管在大多數情況下,我會想象它也不會有明顯的效果。

也許你可以有一個「調試」標誌,並用它作爲這樣的:

static boolean debug = true; 

if (debug) { 
    // loop here 
} 
+0

你能澄清你的意思嗎?說循環「會運行」但JIT會「照顧它」?這聽起來像是一個矛盾。 PS。我對JIT系統只有一點點的瞭解,所以我可能會混淆一些術語。如果我繼續使用Java,這是我需要審查的其中一件事。 –

+0

@ adam.r當然:我的意思是即使斷言關閉(即,如果您用'javac -p'查看字節碼,那麼您將看到循環)循環將存在於字節碼級別(實際上,斷言是啓用時運行代碼,而不是編譯),所以它基本上相當於只有一個空體循環。在這種情況下,當循環執行時,JIT足夠聰明,可以檢測循環中什麼都沒有發生,並優化它。 – arshajii

+0

你的第二個循環並不是很微不足道,這就是爲什麼我建議最好自己來定時,如果這是真正的問題。 – arshajii

相關問題