我目前優化低級別的圖書館,找到了一個反直覺的情況下快10%。導致此問題的提交是here。C#爲什麼使用實例方法委託分配GC0臨時對象,但不是緩存委託
有一個代表
public delegate void FragmentHandler(UnsafeBuffer buffer, int offset, int length, Header header);
和實例方法
public void OnFragment(IDirectBuffer buffer, int offset, int length, Header header)
{
_totalBytes.Set(_totalBytes.Get() + length);
}
在this line,如果我使用的方法作爲代表,該程序分配的臨時委託包裝許多GC0,但性能提高10%(但不穩定)。
var fragmentsRead = image.Poll(OnFragment, MessageCountLimit);
如果我不是在緩存外循環委託這樣的方法:
FragmentHandler onFragmentHandler = OnFragment;
則程序不分配在所有的,數字是很穩定,但慢得多。
我通過產生IL一看,這是做同樣的事情,但在後一種情況下,如果newobj
裝入只調用一次,然後局部變量。
使用緩存的委託IL_0034:
IL_002d: ldarg.0
IL_002e: ldftn instance void Adaptive.Aeron.Samples.IpcThroughput.IpcThroughput/Subscriber::OnFragment(class [Adaptive.Agrona]Adaptive.Agrona.IDirectBuffer, int32, int32, class [Adaptive.Aeron]Adaptive.Aeron.LogBuffer.Header)
IL_0034: newobj instance void [Adaptive.Aeron]Adaptive.Aeron.LogBuffer.FragmentHandler::.ctor(object, native int)
IL_0039: stloc.3
IL_003a: br.s IL_005a
// loop start (head: IL_005a)
IL_003c: ldloc.0
IL_003d: ldloc.3
IL_003e: ldsfld int32 Adaptive.Aeron.Samples.IpcThroughput.IpcThroughput::MessageCountLimit
IL_0043: callvirt instance int32 [Adaptive.Aeron]Adaptive.Aeron.Image::Poll(class [Adaptive.Aeron]Adaptive.Aeron.LogBuffer.FragmentHandler, int32)
IL_0048: stloc.s fragmentsRead
隨着溫度的分配IL_0037:
IL_002c: stloc.2
IL_002d: br.s IL_0058
// loop start (head: IL_0058)
IL_002f: ldloc.0
IL_0030: ldarg.0
IL_0031: ldftn instance void Adaptive.Aeron.Samples.IpcThroughput.IpcThroughput/Subscriber::OnFragment(class [Adaptive.Agrona]Adaptive.Agrona.IDirectBuffer, int32, int32, class [Adaptive.Aeron]Adaptive.Aeron.LogBuffer.Header)
IL_0037: newobj instance void [Adaptive.Aeron]Adaptive.Aeron.LogBuffer.FragmentHandler::.ctor(object, native int)
IL_003c: ldsfld int32 Adaptive.Aeron.Samples.IpcThroughput.IpcThroughput::MessageCountLimit
IL_0041: callvirt instance int32 [Adaptive.Aeron]Adaptive.Aeron.Image::Poll(class [Adaptive.Aeron]Adaptive.Aeron.LogBuffer.FragmentHandler, int32)
IL_0046: stloc.s fragmentsRead
爲什麼與分配的代碼是更快嗎?需要什麼來避免分配,但保持表現?
(上.NET 4.5.2/4.6.1,64,推出,在兩個不同的機器上測試)
更新
下面是獨立的例子,它相當於預期:緩存委託執行更比4秒和11秒快兩倍。所以這個問題是特定於被引用的項目的 - JIT編譯器或其他什麼可能會導致意外結果的細微問題?
using System;
using System.Diagnostics;
namespace TestCachedDelegate {
public delegate int TestDelegate(int first, int second);
public static class Program {
static void Main(string[] args)
{
var tc = new TestClass();
tc.Run();
}
public class TestClass {
public void Run() {
var sw = new Stopwatch();
sw.Restart();
for (int i = 0; i < 1000000000; i++) {
CallDelegate(Add, i, i);
}
sw.Stop();
Console.WriteLine("Non-cached: " + sw.ElapsedMilliseconds);
sw.Restart();
TestDelegate dlgCached = Add;
for (int i = 0; i < 1000000000; i++) {
CallDelegate(dlgCached, i, i);
}
sw.Stop();
Console.WriteLine("Cached: " + sw.ElapsedMilliseconds);
Console.ReadLine();
}
public int CallDelegate(TestDelegate dlg, int first, int second) {
return dlg(first, second);
}
public int Add(int first, int second) {
return first + second;
}
}
}
}
我建議您發佈的問題,就是不相關的領域特定代碼的[MCVE],所以這可以在其他機器上覆制。 –
您的提交有點不清楚,引入了一個與類字段名稱相同的變量,除了前導下劃線以及引用類字段的舊版本。你確定你已經用局部變量進行了測試,我希望? – hvd
@hvd循環內的局部變量的行爲與使用'.Poll()'函數內的方法相同。循環外部的局部變量的行爲與字段類似。 –