Want to show your appreciation?
Please a cup of tea.
Showing posts with label Threading. Show all posts
Showing posts with label Threading. Show all posts

Monday, June 08, 2009

How to Detected If Current Thread Is Interrupted in .Net

When porting an application from Java to C#. The missing of Thread.IsInterrupted in .Net making me to look for some alternatives. At this moment, I'm using the code below but if anyone come up with a better idea please let me know.

        public static bool IsCurrentThreadInterrupted()
        {
            try
            {
                Thread.Sleep(0); // get exception if interrupted.
            }
            catch (ThreadInterruptedException)
            {
                return true;
            }
            return false;
        }

Update (6/9/2009): Also found interesting article about Thread.Sleep:

Monday, May 25, 2009

AtomicInteger vs. Synchronized Monitor

Update (2/7/2010): I noticed a lot of Google traffic landing this page looking for .Net version of AtomicInteger. This is now available as part of the Java’s concurrent API port to .Net, you can download a Bate version here.

When porting the Java's AtomicInteger to .Net, I did a performance test to compare between the implementation using Interlocked and Monitor. The surprised finding inspired me to do a similar test in Java to see how does Java performs.

Let's start with below interface.

public interface AtomicTest {
	public int get();
	public void set(int value);
	public boolean compareAndSet(int expected, int newValue);
	public int incrementAndGet();
	public int decrementAndGet();
}

And we are going to have two implementations, one users AtomicInterger:

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerTest extends AtomicInteger implements AtomicTest {}

Another one uses synchronized access to a volatile field. Except that the read access is not synchronized.

public class MonitorAtomicTest implements AtomicTest {
	private volatile int _value;
	public synchronized boolean compareAndSet(int expected, int newValue) {
		if (expected==_value) {
			_value = newValue;
			return true;
		}
		return false;
	}

	public synchronized int decrementAndGet() {
		return --_value;
	}

	public int get() {
		return _value;
	}

	public synchronized int incrementAndGet() {
		return ++_value;
	}

	public synchronized void set(int value) {
		_value = value;
	}
}

Similar to the .Net test, we run below methods for each implementation with loop set to one million.

    private void runCompareAndSet() {
        int result1, result2, result3;
        for (int i = loop - 1; i >= 0; i--) {
            atomic.compareAndSet(100, 50);
            result1 = atomic.get();
            atomic.compareAndSet(50, 100);
            result2 = atomic.get();
            atomic.compareAndSet(100, 50);
            result3 = atomic.get();
        }
    }

    private void runIncrement() {
        int result1, result2, result3;
        for (int i = loop - 1; i >= 0; i--) {
            atomic.incrementAndGet();
            result1 = atomic.get();
            atomic.incrementAndGet();
            result2 = atomic.get();
            atomic.incrementAndGet();
            result3 = atomic.get();
        }
    }

Finally, we run each method above in multiple threads in parallel. Amount of thread can be passed as command line parameter. Below is the main method, for the detail of how things get done, the full source code is available here.

    public static void main(String[] args)
        throws InterruptedException 
    {
        if (args.length > 0) threadCount = Integer.parseInt(args[0]);
        verbose = "true".equalsIgnoreCase(System.getProperty("verbose"));

        TestRunner a = new TestRunner(new AtomicIntegerTest());
        TestRunner b = new TestRunner(new MonitorAtomicTest());

        a.runCompareAndSetInParallel();
        b.runCompareAndSetInParallel();
        a.runIncrementInParallel();
        b.runIncrementInParallel();
    }

Ran the test on three Windows boxes with lated JRE which is 1.6.0_13. Detail of test hardware and OS can be found in my previous post about similar test for .Net.

Below I listed the test result:

Test result on a two(2) CPU box with client VM

D:\>java -jar AtomicIntegerVsMonitor.jar 1 
  AtomicIntegerTest.runCompareAndSet (ns):   109 Average,   109 Minimal,   109 Maxmial,  1 Threads
  MonitorAtomicTest.runCompareAndSet (ns):   219 Average,   219 Minimal,   219 Maxmial,  1 Threads
  AtomicIntegerTest.runIncrement     (ns):   125 Average,   125 Minimal,   125 Maxmial,  1 Threads
  MonitorAtomicTest.runIncrement     (ns):   250 Average,   250 Minimal,   250 Maxmial,  1 Threads
	
D:\>java -jar AtomicIntegerVsMonitor.jar 1 
  AtomicIntegerTest.runCompareAndSet (ns):   109 Average,   109 Minimal,   109 Maxmial,  1 Threads
  MonitorAtomicTest.runCompareAndSet (ns):   219 Average,   219 Minimal,   219 Maxmial,  1 Threads
  AtomicIntegerTest.runIncrement     (ns):   125 Average,   125 Minimal,   125 Maxmial,  1 Threads
  MonitorAtomicTest.runIncrement     (ns):   265 Average,   265 Minimal,   265 Maxmial,  1 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 2 
  AtomicIntegerTest.runCompareAndSet (ns):   211 Average,   203 Minimal,   219 Maxmial,  2 Threads
  MonitorAtomicTest.runCompareAndSet (ns):  1359 Average,  1297 Minimal,  1422 Maxmial,  2 Threads
  AtomicIntegerTest.runIncrement     (ns):   312 Average,   312 Minimal,   312 Maxmial,  2 Threads
  MonitorAtomicTest.runIncrement     (ns):  1461 Average,  1453 Minimal,  1469 Maxmial,  2 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 2 
  AtomicIntegerTest.runCompareAndSet (ns):   203 Average,   203 Minimal,   203 Maxmial,  2 Threads
  MonitorAtomicTest.runCompareAndSet (ns):  1390 Average,  1375 Minimal,  1406 Maxmial,  2 Threads
  AtomicIntegerTest.runIncrement     (ns):   313 Average,   313 Minimal,   313 Maxmial,  2 Threads
  MonitorAtomicTest.runIncrement     (ns):  1359 Average,  1359 Minimal,  1359 Maxmial,  2 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 4 
  AtomicIntegerTest.runCompareAndSet (ns):   449 Average,   406 Minimal,   485 Maxmial,  4 Threads
  MonitorAtomicTest.runCompareAndSet (ns):  2590 Average,  2469 Minimal,  2641 Maxmial,  4 Threads
  AtomicIntegerTest.runIncrement     (ns):   582 Average,   562 Minimal,   625 Maxmial,  4 Threads
  MonitorAtomicTest.runIncrement     (ns):  2624 Average,  2406 Minimal,  2765 Maxmial,  4 Threads
	
D:\>java -jar AtomicIntegerVsMonitor.jar 4 
  AtomicIntegerTest.runCompareAndSet (ns):   300 Average,   235 Minimal,   406 Maxmial,  4 Threads
  MonitorAtomicTest.runCompareAndSet (ns):  2649 Average,  2532 Minimal,  2797 Maxmial,  4 Threads
  AtomicIntegerTest.runIncrement     (ns):   602 Average,   563 Minimal,   641 Maxmial,  4 Threads
  MonitorAtomicTest.runIncrement     (ns):  2871 Average,  2766 Minimal,  2953 Maxmial,  4 Threads
	
D:\>java -jar AtomicIntegerVsMonitor.jar 16 
  AtomicIntegerTest.runCompareAndSet (ns):  1305 Average,   906 Minimal,  1703 Maxmial, 16 Threads
  MonitorAtomicTest.runCompareAndSet (ns):  9507 Average,  5344 Minimal, 10610 Maxmial, 16 Threads
  AtomicIntegerTest.runIncrement     (ns):  2064 Average,  1516 Minimal,  2516 Maxmial, 16 Threads
  MonitorAtomicTest.runIncrement     (ns):  9944 Average,  8312 Minimal, 10703 Maxmial, 16 Threads
	
D:\>java -jar AtomicIntegerVsMonitor.jar 16 
  AtomicIntegerTest.runCompareAndSet (ns):  1309 Average,   984 Minimal,  1625 Maxmial, 16 Threads
  MonitorAtomicTest.runCompareAndSet (ns): 10958 Average,  8485 Minimal, 12188 Maxmial, 16 Threads
  AtomicIntegerTest.runIncrement     (ns):  2093 Average,  1188 Minimal,  2515 Maxmial, 16 Threads
  MonitorAtomicTest.runIncrement     (ns): 12046 Average, 11188 Minimal, 13110 Maxmial, 16 Threads

Test result on a four(4) CPU box with client VM

D:\>java -jar AtomicIntegerVsMonitor.jar 1
  AtomicIntegerTest.runCompareAndSet (ns):   203 Average,   203 Minimal,   203 Maxmial,  1 Threads
  MonitorAtomicTest.runCompareAndSet (ns):   516 Average,   516 Minimal,   516 Maxmial,  1 Threads
  AtomicIntegerTest.runIncrement     (ns):   218 Average,   218 Minimal,   218 Maxmial,  1 Threads
  MonitorAtomicTest.runIncrement     (ns):   563 Average,   563 Minimal,   563 Maxmial,  1 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 1
  AtomicIntegerTest.runCompareAndSet (ns):   219 Average,   219 Minimal,   219 Maxmial,  1 Threads
  MonitorAtomicTest.runCompareAndSet (ns):   531 Average,   531 Minimal,   531 Maxmial,  1 Threads
  AtomicIntegerTest.runIncrement     (ns):   219 Average,   219 Minimal,   219 Maxmial,  1 Threads
  MonitorAtomicTest.runIncrement     (ns):   578 Average,   578 Minimal,   578 Maxmial,  1 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 2
  AtomicIntegerTest.runCompareAndSet (ns):  1047 Average,  1047 Minimal,  1047 Maxmial,  2 Threads
  MonitorAtomicTest.runCompareAndSet (ns):  4930 Average,  4922 Minimal,  4938 Maxmial,  2 Threads
  AtomicIntegerTest.runIncrement     (ns):   929 Average,   922 Minimal,   937 Maxmial,  2 Threads
  MonitorAtomicTest.runIncrement     (ns):  4891 Average,  4891 Minimal,  4891 Maxmial,  2 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 2
  AtomicIntegerTest.runCompareAndSet (ns):   890 Average,   890 Minimal,   890 Maxmial,  2 Threads
  MonitorAtomicTest.runCompareAndSet (ns):  3922 Average,  3922 Minimal,  3922 Maxmial,  2 Threads
  AtomicIntegerTest.runIncrement     (ns):   961 Average,   953 Minimal,   969 Maxmial,  2 Threads
  MonitorAtomicTest.runIncrement     (ns):  4570 Average,  4562 Minimal,  4578 Maxmial,  2 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 4
  AtomicIntegerTest.runCompareAndSet (ns):  2558 Average,  2484 Minimal,  2594 Maxmial,  4 Threads
  MonitorAtomicTest.runCompareAndSet (ns): 15445 Average, 15375 Minimal, 15547 Maxmial,  4 Threads
  AtomicIntegerTest.runIncrement     (ns):  5359 Average,  5250 Minimal,  5406 Maxmial,  4 Threads
  MonitorAtomicTest.runIncrement     (ns): 15269 Average, 15141 Minimal, 15344 Maxmial,  4 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 4
  AtomicIntegerTest.runCompareAndSet (ns):  2573 Average,  2515 Minimal,  2593 Maxmial,  4 Threads
  MonitorAtomicTest.runCompareAndSet (ns): 15363 Average, 14969 Minimal, 15516 Maxmial,  4 Threads
  AtomicIntegerTest.runIncrement     (ns):  5296 Average,  5250 Minimal,  5328 Maxmial,  4 Threads
  MonitorAtomicTest.runIncrement     (ns): 15823 Average, 15734 Minimal, 15890 Maxmial,  4 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 16
  AtomicIntegerTest.runCompareAndSet (ns):  8722 Average,  6859 Minimal, 10047 Maxmial, 16 Threads
  MonitorAtomicTest.runCompareAndSet (ns): 60182 Average, 57750 Minimal, 63984 Maxmial, 16 Threads
  AtomicIntegerTest.runIncrement     (ns): 18828 Average, 12032 Minimal, 20672 Maxmial, 16 Threads
  MonitorAtomicTest.runIncrement     (ns): 59987 Average, 58516 Minimal, 60594 Maxmial, 16 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 16
  AtomicIntegerTest.runCompareAndSet (ns):  8216 Average,  4828 Minimal, 10219 Maxmial, 16 Threads
  MonitorAtomicTest.runCompareAndSet (ns): 68320 Average, 67188 Minimal, 68719 Maxmial, 16 Threads
  AtomicIntegerTest.runIncrement     (ns): 18796 Average, 14719 Minimal, 21469 Maxmial, 16 Threads
  MonitorAtomicTest.runIncrement     (ns): 67786 Average, 66844 Minimal, 68281 Maxmial, 16 Threads

Test result on a sixteen(16) CPU box with server VM

D:\>java -jar AtomicIntegerVsMonitor.jar 1
  AtomicIntegerTest.runCompareAndSet (ns):    94 Average,    94 Minimal,    94 Maxmial,  1 Threads
  MonitorAtomicTest.runCompareAndSet (ns):   250 Average,   250 Minimal,   250 Maxmial,  1 Threads
  AtomicIntegerTest.runIncrement     (ns):   109 Average,   109 Minimal,   109 Maxmial,  1 Threads
  MonitorAtomicTest.runIncrement     (ns):   250 Average,   250 Minimal,   250 Maxmial,  1 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 1
  AtomicIntegerTest.runCompareAndSet (ns):    94 Average,    94 Minimal,    94 Maxmial,  1 Threads
  MonitorAtomicTest.runCompareAndSet (ns):   250 Average,   250 Minimal,   250 Maxmial,  1 Threads
  AtomicIntegerTest.runIncrement     (ns):   109 Average,   109 Minimal,   109 Maxmial,  1 Threads
  MonitorAtomicTest.runIncrement     (ns):   234 Average,   234 Minimal,   234 Maxmial,  1 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 2
  AtomicIntegerTest.runCompareAndSet (ns):   875 Average,   875 Minimal,   875 Maxmial,  2 Threads
  MonitorAtomicTest.runCompareAndSet (ns):  2461 Average,  2453 Minimal,  2469 Maxmial,  2 Threads
  AtomicIntegerTest.runIncrement     (ns):  1063 Average,  1063 Minimal,  1063 Maxmial,  2 Threads
  MonitorAtomicTest.runIncrement     (ns):  2382 Average,  2375 Minimal,  2390 Maxmial,  2 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 2
  AtomicIntegerTest.runCompareAndSet (ns):  1829 Average,  1829 Minimal,  1829 Maxmial,  2 Threads
  MonitorAtomicTest.runCompareAndSet (ns):  2828 Average,  2828 Minimal,  2828 Maxmial,  2 Threads
  AtomicIntegerTest.runIncrement     (ns):   422 Average,   391 Minimal,   453 Maxmial,  2 Threads
  MonitorAtomicTest.runIncrement     (ns):  3258 Average,  3250 Minimal,  3266 Maxmial,  2 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 4
  AtomicIntegerTest.runCompareAndSet (ns):  3250 Average,  3250 Minimal,  3250 Maxmial,  4 Threads
  MonitorAtomicTest.runCompareAndSet (ns):  6097 Average,  5953 Minimal,  6156 Maxmial,  4 Threads
  AtomicIntegerTest.runIncrement     (ns):  2531 Average,  2531 Minimal,  2531 Maxmial,  4 Threads
  MonitorAtomicTest.runIncrement     (ns):  5808 Average,  5766 Minimal,  5844 Maxmial,  4 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 4
  AtomicIntegerTest.runCompareAndSet (ns):  3000 Average,  3000 Minimal,  3000 Maxmial,  4 Threads
  MonitorAtomicTest.runCompareAndSet (ns):  6097 Average,  5922 Minimal,  6187 Maxmial,  4 Threads
  AtomicIntegerTest.runIncrement     (ns):  2547 Average,  2547 Minimal,  2547 Maxmial,  4 Threads
  MonitorAtomicTest.runIncrement     (ns):  6039 Average,  5969 Minimal,  6094 Maxmial,  4 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 16
  AtomicIntegerTest.runCompareAndSet (ns):  9749 Average,  9749 Minimal,  9749 Maxmial,  16 Threads
  MonitorAtomicTest.runCompareAndSet (ns): 21486 Average, 21078 Minimal, 21641 Maxmial,  16 Threads
  AtomicIntegerTest.runIncrement     (ns): 46216 Average, 45562 Minimal, 46827 Maxmial,  16 Threads
  MonitorAtomicTest.runIncrement     (ns): 24795 Average, 23422 Minimal, 25281 Maxmial,  16 Threads

D:\>java -jar AtomicIntegerVsMonitor.jar 16
  AtomicIntegerTest.runCompareAndSet (ns):  9787 Average,  9781 Minimal,  9797 Maxmial,  16 Threads
  MonitorAtomicTest.runCompareAndSet (ns): 23269 Average, 23109 Minimal, 23390 Maxmial,  16 Threads
  AtomicIntegerTest.runIncrement     (ns): 45608 Average, 45047 Minimal, 46141 Maxmial,  16 Threads
  MonitorAtomicTest.runIncrement     (ns): 19401 Average, 19156 Minimal, 19578 Maxmial,  16 Threads

This test is different from the .Net test. It is quite conclusive:

  • AtomicInteger performs consistently better then Monitor except one test case when running Increment with 16 threads on 16 CUP box.
  • Java's AtomicInteger.compareAndSet performs in peer with .Net's Interlocked.CompareExchange.
  • Java's Monitor is consistently slower then .Net's and getting worse when there are more threads and more collisions.
  • AtomicInteger also suffers from heavy collision as Interlocked does. One extreme case is the Increment test with 16 threads on 16 CPU box. The implementation of AtomicInteger.incrementAndGet uses a looped try of AtomicInteger.compareAndSet, that is how the collision increased. The result of Interlocked.Increment is much better so I believe a different strategy is in use.

Sunday, May 24, 2009

Interlocked vs. Monitor Performance

In .Net, when I need thread safe access to a field, I can either use Monitor or use Interlocked class. I have been in believe that the Interlocked should work faster, otherwise I don't see the value of using it except for inter-process atomic. Today when I was porting the Java's AtomicInteger to .Net, I was wondering how should I implement this. I again faced the choice between Interlocked and Monitor. So I decided to do a test.

The test goes against three different implementations of below interface:

    internal interface IAtomic
    {
        int Value { get; set; }
        int CompareExchange(int newValue, int expected);
        int Exchange(int newValue);
    }

One implementation uses method in the Interlocked static class to operate on an volatile field to provide thread safe access to the integer.

    internal class InterlockAtomic : IAtomic
    {
        private volatile int _value = 50;

        public int Value
        {
            get { return _value; }
            set { _value = value; }
        }

        public virtual int CompareExchange(int newValue, int expected)
        {
            return Interlocked.CompareExchange(ref _value, newValue, expected);
        }

        public virtual int Exchange(int newValue)
        {
            return Interlocked.Exchange(ref _value, newValue);
        }
    }

The 2nd implementation uses Monitor with C#'s lock keyword for all the access.

    internal class MonitorAtomic : IAtomic
    {

        private int _value = 50;

        public int Value
        {
            get { lock (this) return _value; }
            set { lock (this) _value = value; }
        }

        public virtual int CompareExchange(int newValue, int expected)
        {
            lock (this)
            {
                int orig = _value;
                if (expected == orig) _value = newValue;
                return orig;
            }
        }

        public virtual int Exchange(int newValue)
        {
            lock (this)
            {
                int orig = _value;
                _value = newValue;
                return orig;
            }
        }
    }

The 3rd implementation uses  Monitor for all access except the read access thread safety is provided by the volatile.

    internal class MonitorVolatileAtomic : IAtomic
    {

        private volatile int _value = 50;

        public int Value
        {
            get { return _value; }
            set { lock (this) _value = value; }
        }

        public virtual int CompareExchange(int newValue, int expected)
        {
            lock (this)
            {
                int orig = _value;
                if (expected == orig) _value = newValue;
                return orig;
            }
        }

        public virtual int Exchange(int newValue)
        {
            lock (this)
            {
                int orig = _value;
                _value = newValue;
                return orig;
            }
        }
    }

Then run the below methods for each implementation with loop set to one million.

        private void RunCompareExchange()
        {
            int result1, result2, result3;
            for (int i = loop - 1; i >= 0; i--)
            {
                _atomic.CompareExchange(50, 100);
                result1 = _atomic.Value;
                _atomic.CompareExchange(100, 50);
                result2 = _atomic.Value;
                _atomic.CompareExchange(50, 100);
                result3 = _atomic.Value;
            }
        }

        private void RunExchange()
        {
            int result1, result2, result3;
            for (int i = loop - 1; i >= 0; i--)
            {
                _atomic.Exchange(30);
                result1 = _atomic.Value;
                _atomic.Exchange(50);
                result2 = _atomic.Value;
                _atomic.Exchange(100);
                result3 = _atomic.Value;
            }
        }

Finally, we run each method above in multiple threads in parallel. Amount of thread can be passed as command line parameter. Below is the main method, for the detail of how things get done, the full source code is available here.

        static void Main(string[] args)
        {
            int threadCount = 1;
            if (args.Length > 0)
            {
                try
                {
                    threadCount = int.Parse(args[0]);
                }
                catch (Exception e)
                {
                    Console.Error.WriteLine(e.Message);
                }
            }
            Console.WriteLine("Using {0} threads:", threadCount);

            var a = new Program { _atomic = new InterlockAtomic() };
            var b = new Program { _atomic = new MonitorAtomic() };
            var c = new Program { _atomic = new MonitorVolatileAtomic() };
            RunAll(a.RunCompareExchange, threadCount);
            RunAll(b.RunCompareExchange, threadCount);
            RunAll(c.RunCompareExchange, threadCount);
            RunAll(a.RunExchange, threadCount);
            RunAll(b.RunExchange, threadCount);
            RunAll(c.RunExchange, threadCount);
        }

I made the Release build and tested on three different machines

  • Thinkpad laptop running Windows XP Professional SP2 with Core 2 Due T7300 2GHz CPU and 4GB dual channel DDR2. I have the systeminfo for 2 CPU box.
  • HP server (quite old) running Windows Server 2003 SE SP1 with 4 Xeon (Not sure but mostly 2x2) 2.8GHz CPUs and 3.5GB (don't know what memory). Here is the systeminfo for 4 CPU box.
  • HP server (very new) running Windows Server 2003 SE x64 SP2 with 16 Xeon (4x4) 3.4GHz CPUs and 16GB of dual channel DDR2 ECC. Here is the systeminfo for the 16 CPU box.

The test result is very interesting and I cannot explain the reason for this.

Here is the result of 2 CPU test.  In this test the Interlocked methods is clearly faster in all situations.

D:\>InterlockVsMonitor.exe 1
Using 1 threads:
          InterlockAtomic.RunCompareExchange   (ns):     81 Average,     81 Minimal,     81 Maxmial
            MonitorAtomic.RunCompareExchange   (ns):    428 Average,    428 Minimal,    428 Maxmial
    MonitorVolatileAtomic.RunCompareExchange   (ns):    254 Average,    254 Minimal,    254 Maxmial
          InterlockAtomic.RunExchange          (ns):     81 Average,     81 Minimal,     81 Maxmial
            MonitorAtomic.RunExchange          (ns):    544 Average,    544 Minimal,    544 Maxmial
    MonitorVolatileAtomic.RunExchange          (ns):    277 Average,    277 Minimal,    277 Maxmial

D:\>InterlockVsMonitor.exe 2
Using 2 threads:
          InterlockAtomic.RunCompareExchange   (ns):    180 Average,    176 Minimal,    184 Maxmial
            MonitorAtomic.RunCompareExchange   (ns):    917 Average,    795 Minimal,   1039 Maxmial
    MonitorVolatileAtomic.RunCompareExchange   (ns):    507 Average,    472 Minimal,    543 Maxmial
          InterlockAtomic.RunExchange          (ns):    227 Average,    222 Minimal,    232 Maxmial
            MonitorAtomic.RunExchange          (ns):   1007 Average,    973 Minimal,   1041 Maxmial
    MonitorVolatileAtomic.RunExchange          (ns):    485 Average,    446 Minimal,    524 Maxmial

D:\>InterlockVsMonitor.exe 4
Using 4 threads:
          InterlockAtomic.RunCompareExchange   (ns):    345 Average,    305 Minimal,    370 Maxmial
            MonitorAtomic.RunCompareExchange   (ns):   1901 Average,   1711 Minimal,   2064 Maxmial
    MonitorVolatileAtomic.RunCompareExchange   (ns):   1048 Average,    925 Minimal,   1101 Maxmial
          InterlockAtomic.RunExchange          (ns):    395 Average,    322 Minimal,    456 Maxmial
            MonitorAtomic.RunExchange          (ns):   1797 Average,   1488 Minimal,   2030 Maxmial
    MonitorVolatileAtomic.RunExchange          (ns):    816 Average,    561 Minimal,   1151 Maxmial

D:\>InterlockVsMonitor.exe 16
Using 16 threads:
          InterlockAtomic.RunCompareExchange   (ns):    998 Average,    736 Minimal,   1424 Maxmial
            MonitorAtomic.RunCompareExchange   (ns):   8315 Average,   3623 Minimal,   9941 Maxmial
    MonitorVolatileAtomic.RunCompareExchange   (ns):   4480 Average,   3345 Minimal,   5104 Maxmial
          InterlockAtomic.RunExchange          (ns):   2051 Average,   1522 Minimal,   2448 Maxmial
            MonitorAtomic.RunExchange          (ns):   9353 Average,   5795 Minimal,  11104 Maxmial
    MonitorVolatileAtomic.RunExchange          (ns):   3419 Average,   1509 Minimal,   4582 Maxmial

In the 4 CPU box, Interlocked start to lose the race when number of parallel thread increases.

D:\>InterlockVsMonitor.exe 1
Using 1 threads:
          InterlockAtomic.RunCompareExchange   (ns):    181 Average,    181 Minimal,    181 Maxmial
            MonitorAtomic.RunCompareExchange   (ns):    853 Average,    853 Minimal,    853 Maxmial
    MonitorVolatileAtomic.RunCompareExchange   (ns):    461 Average,    461 Minimal,    461 Maxmial
          InterlockAtomic.RunExchange          (ns):    179 Average,    179 Minimal,    179 Maxmial
            MonitorAtomic.RunExchange          (ns):    796 Average,    796 Minimal,    796 Maxmial
    MonitorVolatileAtomic.RunExchange          (ns):    441 Average,    441 Minimal,    441 Maxmial

D:\>InterlockVsMonitor.exe 2
Using 2 threads:
          InterlockAtomic.RunCompareExchange   (ns):    864 Average,    863 Minimal,    865 Maxmial
            MonitorAtomic.RunCompareExchange   (ns):   1984 Average,   1965 Minimal,   2003 Maxmial
    MonitorVolatileAtomic.RunCompareExchange   (ns):    948 Average,    870 Minimal,   1026 Maxmial
          InterlockAtomic.RunExchange          (ns):   1155 Average,   1155 Minimal,   1156 Maxmial
            MonitorAtomic.RunExchange          (ns):   1852 Average,   1768 Minimal,   1936 Maxmial
    MonitorVolatileAtomic.RunExchange          (ns):    925 Average,    852 Minimal,    999 Maxmial

D:\>InterlockVsMonitor.exe 4
Using 4 threads:
          InterlockAtomic.RunCompareExchange   (ns):   2558 Average,   2539 Minimal,   2575 Maxmial
            MonitorAtomic.RunCompareExchange   (ns):   4553 Average,   4241 Minimal,   5198 Maxmial
    MonitorVolatileAtomic.RunCompareExchange   (ns):   2502 Average,   2438 Minimal,   2543 Maxmial
          InterlockAtomic.RunExchange          (ns):   4809 Average,   4748 Minimal,   4870 Maxmial
            MonitorAtomic.RunExchange          (ns):   4659 Average,   4504 Minimal,   4780 Maxmial
    MonitorVolatileAtomic.RunExchange          (ns):   2455 Average,   2378 Minimal,   2509 Maxmial

D:\>InterlockVsMonitor.exe 16
Using 16 threads:
          InterlockAtomic.RunCompareExchange   (ns):   9238 Average,   7494 Minimal,  10111 Maxmial
            MonitorAtomic.RunCompareExchange   (ns):  17039 Average,  12189 Minimal,  18937 Maxmial
    MonitorVolatileAtomic.RunCompareExchange   (ns):   9110 Average,   6562 Minimal,  10364 Maxmial
          InterlockAtomic.RunExchange          (ns):  12504 Average,   5275 Minimal,  18905 Maxmial
            MonitorAtomic.RunExchange          (ns):  17205 Average,  11394 Minimal,  19518 Maxmial
    MonitorVolatileAtomic.RunExchange          (ns):   8934 Average,   7105 Minimal,  10300 Maxmial

In the 16 CPU box, Interlocked can only win the game in single thread test. Interlocked is nearly 4 times slow in the 2 and 4 thread test. And somehow get close in the 16 thread test.

D:\>InterlockVsMonitor.exe 1
Using 1 threads:
          InterlockAtomic.RunCompareExchange   (ns):    138 Average,    138 Minimal,    138 Maxmial
            MonitorAtomic.RunCompareExchange   (ns):    463 Average,    463 Minimal,    463 Maxmial
    MonitorVolatileAtomic.RunCompareExchange   (ns):    311 Average,    311 Minimal,    311 Maxmial
          InterlockAtomic.RunExchange          (ns):    133 Average,    133 Minimal,    133 Maxmial
            MonitorAtomic.RunExchange          (ns):    457 Average,    457 Minimal,    457 Maxmial
    MonitorVolatileAtomic.RunExchange          (ns):    257 Average,    257 Minimal,    257 Maxmial

D:\>InterlockVsMonitor.exe 2
Using 2 threads:
          InterlockAtomic.RunCompareExchange   (ns):   1855 Average,   1855 Minimal,   1855 Maxmial
            MonitorAtomic.RunCompareExchange   (ns):    876 Average,    873 Minimal,    879 Maxmial
    MonitorVolatileAtomic.RunCompareExchange   (ns):    482 Average,    448 Minimal,    517 Maxmial
          InterlockAtomic.RunExchange          (ns):   1821 Average,   1821 Minimal,   1822 Maxmial
            MonitorAtomic.RunExchange          (ns):    825 Average,    760 Minimal,    891 Maxmial
    MonitorVolatileAtomic.RunExchange          (ns):    501 Average,    498 Minimal,    505 Maxmial

D:\>InterlockVsMonitor.exe 4
Using 4 threads:
          InterlockAtomic.RunCompareExchange   (ns):   4158 Average,   4158 Minimal,   4160 Maxmial
            MonitorAtomic.RunCompareExchange   (ns):   1763 Average,   1731 Minimal,   1815 Maxmial
    MonitorVolatileAtomic.RunCompareExchange   (ns):    955 Average,    929 Minimal,    998 Maxmial
          InterlockAtomic.RunExchange          (ns):   4192 Average,   4172 Minimal,   4199 Maxmial
            MonitorAtomic.RunExchange          (ns):   1766 Average,   1628 Minimal,   1824 Maxmial
    MonitorVolatileAtomic.RunExchange          (ns):    948 Average,    786 Minimal,   1016 Maxmial

D:\>InterlockVsMonitor.exe 16
Using 16 threads:
          InterlockAtomic.RunCompareExchange   (ns):   8399 Average,   8347 Minimal,   8435 Maxmial
            MonitorAtomic.RunCompareExchange   (ns):  11881 Average,  11595 Minimal,  12082 Maxmial
    MonitorVolatileAtomic.RunCompareExchange   (ns):   7296 Average,   6994 Minimal,   7411 Maxmial
          InterlockAtomic.RunExchange          (ns):   8214 Average,   8180 Minimal,   8257 Maxmial
            MonitorAtomic.RunExchange          (ns):  11984 Average,  11556 Minimal,  12197 Maxmial
    MonitorVolatileAtomic.RunExchange          (ns):   7086 Average,   6707 Minimal,   7327 Maxmial

I don't have good explanation to the test result. I don't understand why the 2 CPU box is the fastest for most of tests. And Monitor performs better then Interlocked in multi-thread test on the box has more CPUs.

Update 5/25/2009: One thing is clear that Interlocked is always the fastest in single thread tests. Given another thought, it indicates to me that I should use Interlocked in the cases where access collision is rarely to occur although it does occasionally. For example, preventing the occasional write from slowing down main thread's excessive write access!?