前进's profile当我不存在PhotosBlogListsMore Tools Help

前进 傅

Lists
No list items have been added yet.
May 09

[Program tip]Field access versus local variable access performance

Field access versus local variable access performance

Recently, I was at a customer site and they were comparing the performance of a function that was written in unmanaged C with its C# equivalent. As they expected, the C code was performing much faster than the C# code. However, I didn't expect this. Once managed code is JIT compiled, the code is very similar to that of unmanaged code. The code was being called in a loop in both versios numerous times so the additional time spent by the JIT compiler should be noise and I expect the performance of the C# code to be near identical and even better than the unmanaged C version because the JIT compiler could emit optimizations for the speciifc CPU architecture of the host machine.

I was very interested in this problem and I closely examined both functions. Not just the source code, but I also very closely examined the machine code generated by the unmanaged C compiler as well as the machine code generated by the .NET Framework's JIT compiler. Not surprising (to me), the machine code was pratically identical. I did see a place where the C compiler emitted an INCrement instruction where th JIT compiler emitted an ADD instruction to add 1. I assume that this is because the JIT compiler knew the host CPU and determined that an ADD instruction would be faster than an INCrement instruction. This would actually make the managed code version faster.

I also noticed a place where the unmanaged version was updating a register directly where the managed version was updating a indirect memory location. This was because the unmanaged source code was updating a local variable while the C# version was turned into an object-oriented equivalent and as updating a field in the class object. This difference alse seemed minor to me and could not possibly accounting for the performance discrepency we were seeing. However, we continued to examine the code and in an effort to get the emitted machine code to be identical between the 2 as possible, we got rid of the field in the object and turned it into a local variable instead. WOW! What a difference this made in performance!

Apparently, the JIT compiler is able to enregister local variables but it cannot enregister fields. This makes sense since a field can actually be manipulated by multiple threads simultaneously or via other means behind a method's back and therefore, the JIT compiler takes the safe route of not enregisterring the local and always saves and fetches its value from memory. On the other hand, a local variable cannot be modified behind a method's back and therefor the JIT compiler can put it in a register.

I always knew that memory access is slower than CPU register access but I guess I nenver really knew how big a difference it could be. On the machine we were using to test this, the differenece was about 8 times. That is, the method with the field ran 8 times slower than the equivalent method with the local variable! On my notebook computer, the difference was much less: the local variable version ran in ~25 seconds while the field version ran in ~31 seconds. Still a pretty big difference.

So, the moral of the story, is that you should avoid field access as much as possible in performance-sensitive code. It might even make sense to copy a field into a loca, use the local in the method and then copy the result back from the local into the field just before the method exits. Below is some simple code (that requires Whidbey because I'm using the Stopwatch class) that you can compile and test on your own machine to see the perf difference:

using System;
using System.Diagnostics;

class App {
static void Main() {
  const Int64 iter = 5000000000;

  Stopwatch sw = Stopwatch.StartNew();
  TestLocalAccess(iter);
  Console.WriteLine("time taken:{0} ticks", sw.Elapsed);

  sw = Stopwatch.StartNew();
  TestFieldAccess(iter);
  Console.WriteLine("time taken:{0} ticks", sw.Elapsed);
}

private static Int64 j;

public static void TestLocalAccess(Int64 numIncrement) {
  Int64 j=0;
  for(Int64 i=0; i<numIncrement; i++) j++;
}

public static void TestFieldAccess(Int64 numIncrement) {

  for(Int64 i=0; i<numIncrement; i++) j++;
}
}

Posted Saturday, November 13, 2004 11:09 PM by JeffreyR | 7 Comments

Filed under: Jeffrey Richter

October 16

走霉运

乘地铁没到站就下车,出门走迷路,换车又丢交通卡,又流鼻血,又鼻子过敏狂打喷嚏,一个字:衰……

September 22

思念是一种折磨……

当看着自己的思念随风飘去,

当感受着自己的心备受煎熬,

当眼前的灯光都变得模糊,

当站在拥挤的车上,竭力控制哽咽,

当无力地抚慰自己的心口,

思念的痛,可以毁掉一切,

眼见着思念侵袭着爱,慢慢把它变做一种恨,

还剩下什么……

September 01

絮絮写真馆……

 

先来张证件照:

U637P55T4D41549F50DT20050201173525

全身照一张:

char_player_004

落絮无声春堕泪,

飞花有梦燕归愁,

金风玉露相逢处,

纵使无语也含羞。

dfbqlsqt_l_1

千万不要招惹美女,下场可是很惨的:

12121

英雄救美:

zlejphwp_l_1 
爱人已逝:

wr

絮絮的本来面目……,一贪吃的小不点……。

2332

August 29

流了好长时间的鼻血

晚上回到家,居然开始流鼻血了。

其实已经习惯了这个鼻子流血,

可是,这次,它不肯停下来,

感觉自己的体液不断地流失而带来的不安,

满眼都是灰色的,

无助,

我是孤独的,

当流鼻血这件事情成为我这个人的一部分的时候,

便不会有人觉得这是件不好的事情,

有人给我一个大大的问号,

有人调侃我,

有人无语。

所谓人是独立的个体,

就是需要忍受独立带来的孤独,

没人能完全体会自己的感受,只有自己。

人心,

总是经历着从开放向封闭的过程,

而从封闭转向开放,那是个多难的过程啊。

人是不甘寂寞的,因为寂寞总是陪伴左右。

 

当我不存在

Windows Media Player

Photo 1 of 14
More albums (1)
No list items have been added yet.