2013-03-14 47 views
2

我使用ILGenerator編寫的數組訪問操作很簡單。當使用這個確切的代碼創建方法時,我打開反彙編,沒關係,沒有數組邊界檢查。DynamicAssembly中的數組邊界檢查僅在評估堆棧爲空時有效

但是,當我第一次將其他類的實例放在評估堆棧上時,然後運行for循環,它會進行數組邊界檢查。我正在發佈。

任何想法爲什麼?我已經閱讀博客文章數組邊界檢查:http://blogs.msdn.com/b/clrcodegeneration/archive/2009/08/13/array-bounds-check-elimination-in-the-clr.aspx

 // Uncomment this to enable bound checks, type of arg0 is some my class 
     //il.Emit(OpCodes.Ldarg_0); 

     var startLbl = il.DefineLabel(); 
     var testLbl = il.DefineLabel(); 
     var index = il.DeclareLocal(typeof(Int32)); 
     var arr = il.DeclareLocal(typeof(Int32).MakeArrayType()); 

     // arr = new int[4]; 
     il.Emit(OpCodes.Ldc_I4_4); 
     il.Emit(OpCodes.Newarr, typeof(Int32)); 
     il.Emit(OpCodes.Stloc, arr); 

     // Index = 0 
     il.Emit(OpCodes.Ldc_I4_0); // Push index 
     il.Emit(OpCodes.Stloc, index); // Pop index, store 

     il.Emit(OpCodes.Br_S, testLbl); // Go to test 

     // Begin for 
     il.MarkLabel(startLbl); 

     // Load array, index 
     il.Emit(OpCodes.Ldloc, arr); 
     il.Emit(OpCodes.Ldloc, index); 

     // Now on stack: array, index 
     // Load element 
     il.Emit(OpCodes.Ldelem_I4); 
     // Nothing here now, later some function call 
     il.Emit(OpCodes.Pop); 

     // Index++ 
     il.Emit(OpCodes.Ldloc, index); 
     il.Emit(OpCodes.Ldc_I4_1); 
     il.Emit(OpCodes.Add); 
     il.Emit(OpCodes.Stloc, index); 

     il.MarkLabel(testLbl); 
     // Load index, count, test for end 
     il.Emit(OpCodes.Ldloc, index); 
     il.Emit(OpCodes.Ldloc, arr); 
     il.Emit(OpCodes.Ldlen); // Push len 
     il.Emit(OpCodes.Conv_I4); // Push len 
     il.Emit(OpCodes.Blt_S, startLbl); 
     // End for 

     // Remove instance added on top 
     //il.Emit(OpCodes.Pop); 

正如我產生IL代碼,最好保持的類的實例上計算堆棧或局部變量?

例如我得到實例,通過領域,每個領域做任何事情,而不是回報。我只是將實例保存在堆棧中,並在讀取下一個字段之前調用Emit(OpCodes.Dup)。但這似乎是錯誤的(至少在上面提到的情況下)。

任何關於生成(高效/格式良好)IL代碼的文章/博客讚賞。

回答

2

在使用當地人一般通常會導致更可讀的代碼更容易調試,這給IL已經不是大多數開發商都用來讀書是很重要的。 JIT甚至有可能會消除可能的性能損失。

從我在ILSpy中看到的,csc更喜歡本地人,儘管我不得不承認,當我查看IL而不是反編譯爲C#時,它主要是調試代碼。由於JIT可能寫的是期望它將主要運行在微軟編譯器的輸出上,如果它不承認循環結構不符合它們的編譯器所發出的結果,那就不足爲奇了。這是非常有道理的,額外的堆棧條目阻礙了JIT識別它可以消除邊界檢查的能力。

0

你是否在釋放模式下運行,調試器在你的方法是Jitted之前未連接?然後再附上? 我知道似乎不需要做這一步,但如果連接調試器,調試器將發出較少的優化代碼。

包括您認爲錯誤所在的整個方法。我建議用這個方法發出一個程序集,以便你可以運行PEVerify。有時你會成爲編譯代碼,但無效。

抖動可以具有與代碼非常硬的時間是無效的(例如,錯誤的堆棧等unboxed T上堆疊預期boxed T上的通用代碼,只有與對象運行。),尤其是無法驗證的是不正常的圖案。 (例如,不會通過C#或C++/CLI發生的不安全代碼)。除非您期待,否則您應始終嘗試在peverify中出現0個錯誤。 (例如calli