簡體   English   中英

GC與C#和C ++在同一解決方案中

[英]GC with C# and C++ in same solution

我有一個由許多C#項目組成的解決方案。 它是用C#編寫的,以使其快速運行。 垃圾收集開始成為一個問題 - 我們正在看到我們想要避免的100毫秒延遲。

一種想法是用C ++重新編寫它,逐個項目。 但是,如果將C#與非托管C ++結合使用,C ++項目中的線程是否也會被垃圾收集凍結?

UPDATE

謝謝你的回復。 事實上,這是一個100毫秒可能很重要的應用程序。 在C#中構建它可能是一個糟糕的決定,但它必須在當時快速啟動並運行。

現在,我們正在使用Windows的多媒體計時器每隔5毫秒觸發一次事件。 我們確實看到了100多個ms的間隙,我們通過檢查GC計數器確認這些間隙總是在收集期間發生。 優化正在進行; 內置於發布模式。

我在一家貿易公司擔任.NET開發人員,與您一樣,我們關心100毫秒的延遲。 當需要可靠的最小延遲時,垃圾收集確實會成為一個重要問題。

也就是說,我不認為遷移到C ++會是一個聰明的舉動,主要是因為它會耗費多少時間。 在堆上分配了一定量的內存后,會發生垃圾收集。 您可以通過最小化代碼創建的堆分配量來大大緩解此問題

我建議您嘗試在應用程序中發現負責大量分配的方法。 構建Anywhere對象將成為修改的候選對象。 打擊垃圾收集的經典方法是利用資源池 :不是每次調用方法時都創建新對象,而是維護已構造對象池,在每次方法調用時從池中借用並將對象返回到池中該方法已經完成。

另一個明智的做法是在代碼中搜索任何ArrayListHashTable或類似的非泛型集合,這些集合是box / unbox值類型,導致完全不必要的堆分配。 盡可能用List<T>Dictionary<TKey, TValue>替換它們(這里我特指的是值類型的集合,如intdoublelong等)。 同樣,請注意您可能正在調用哪些方法值類型參數(或返回盒裝值類型)。

這些只是您可以采取的幾個相對較小的步驟來減少垃圾收集計數,但它們可以產生很大的不同。 通過足夠的努力,甚至可以在應用程序的連續操作階段(除了啟動和關閉之外的所有階段)完全(或至少幾乎 )消除所有第2代垃圾收集。 而且我認為你會發現第2代系列是真正的重擊手。

這篇文章概述了一家公司通過資源池最小化.NET應用程序延遲的努力,以及其他幾種方法,並取得了巨大成功:

Rapid Addition利用Microsoft .NET 3.5 Framework構建超低延遲的FIX和FAST處理

所以重申:我強烈建議調查修改代碼的方法,以便減少垃圾收集而不是轉換為完全不同的語言。

首先,您是否嘗試過分析事物以查看是否可以優化內存使用量? 一個好的起點是使用CLR分析器 (適用於最高3.5的所有CLR)。

用C ++重寫所有東西是一個令人難以置信的巨大變化,只是為了小的性能打擊 - 這就像通過截斷你的手來修復剪紙一樣。

您確定那些100毫秒延遲是由於GC造成的嗎? 在你花費大量的時間,精力和金錢重寫C ++中的東西之前,我會非常確定GC確實是你的問題。 將托管代碼與非托管代碼相結合也會出現問題,因為您必須處理這兩個上下文之間的編組。 這將增加其自身的性能消耗,並且您的凈收益最終很可能最終為零。

我會描述你的C#應用​​程序,並縮小你的100ms延遲的確切位置。 此工具可能會有所幫助:

如何:使用CLR Profiler

關於GC的一個詞

關於.NET GC的另一個詞(或者實際上任何GC,就此而言。)這個經常說不夠,但它是用GC成功編寫代碼的關鍵因素:

有一個垃圾收集並不意味着你不必考慮內存管理!

編寫與GC完美匹配的最佳代碼比編寫與非托管堆良好匹配的C ++代碼需要更少的工作量和麻煩......但是您仍然必須理解GC並編寫與其良好匹配的代碼。 您不能完全忽略所有與內存管理相關的事情。 你不得不擔心它,但你仍然需要考慮它。 編寫與GC完美匹配的代碼是實現不會產生內存管理問題的高性能代碼的一個至關重要的因素。

下面的文章也應該有所幫助,因為它概述了.NET GC的基本行為(通過.NET 3.5有效...很可能本文不再完全適用於.NET 4.0,因為有一些關鍵的變化到它的GC ...對於一個,它不再需要在收集時阻止.NET線程):

垃圾收集:Microsoft .NET Framework中的自動內存管理

CLR GC不會在集合期間掛起運行非托管代碼的線程。 如果本機代碼調用托管代碼,或返回托管代碼,那么它可能會受到集合的影響(就像任何其他托管代碼一樣)。

如果100毫秒是一個問題,我asusme您的代碼是關鍵任務。 混合托管和非托管代碼將在托管應用程序域和非托管空間之間進行調用。

GC已經過優化,所以在此之前嘗試分析您的代碼並重構它。 如果您擔心GC,請嘗試使用設置線程優先級並盡可能減少對象創建並盡可能緩存數據。 在您的項目中,屬性也會啟用Optimize代碼設置。

一種想法是用C ++重新編寫它,逐個項目。 但是,如果將C#與非托管C ++結合使用,C ++項目中的線程是否也會被垃圾收集凍結?

如果C ++代碼在不同的線程上運行則不行。 C ++堆和托管堆是不同的東西。

另一方面,如果您的C ++代碼執行了大量的新/刪除操作,那么當堆變為碎片時,您仍將開始在C ++代碼中看到分配停頓。 這些檔位可能比你在C#代碼中看到的糟糕得多,因為沒有GC。 當需要清理堆時,它只發生在對new或delete的調用中。

如果您確實有嚴格的性能要求,那么您需要計划不在時間關鍵代碼中的常規堆中進行任何內存分配。 在實踐中,這意味着這將更像C代碼而不是C ++代碼,或者使用特殊的內存池和新的放置。

.NET 4.0具有所謂的背景垃圾收集 ,它與並發垃圾收集不同,它可能是導致您的問題的原因。 Jason Olson與Carl Franklin和Richard Campbell就.NET Rocks 第517集進行了討論 你可以在這里查看成績單。 它在第5頁。

我不完全確定是否只是升級到4.0 Framework會解決你的問題,但我想在你用C ++重寫所有內容之前考慮它是值得的。

暫無
暫無

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

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