Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Performance of thread context switching in C#

Status
Not open for further replies.

hjkhjk

Instructor
Aug 23, 2002
1
0
0
US
I am new to C# and have a Java-based application that I would like to port to C#. The application can involve thousands of independent worker threads only one of which is in a runnable state at a particular time. An executive thread coordinates the sequencing of execution of the worker threads. This is not a typical multithreaded application so the normal caveats about too many threads do not apply.

With a prototype Java implementation, I can get a total of ~10000 threads and achieve context switching speeds of ~80000 switches/sec.on a 1.4GHz Athlon based PC with 256MB memory.

I took the Java prototype code and converted it to C# using Microsoft's Java Language Conversion Assitant utility.

When I run the program on the same platform, I get the same 10000 threads but the context switching in C# is on the order of 5000 switches/sec. which is an order of magnitude slower than in Java. Even with few threads, the context switches in C# are performed at ~5K/sec.

There is almost a 1-1 correspondence between the Java implementation and the C# implementation.
It appears both runtime environments use the same underlying Windows OS thread model so why is C# so much slower? It appears the problem is related to the acquistion and relinquishment of an object locks in C#. Following is the sample code. It takes the following two command line arguments:

1) number of threads
2) number of context switches to perform

The executive thread creates the specified number of threads and then simply causes each to execute in order (one at a time) by notifying each and then, in turn, waiting to be notified when the worker thread is finished executing. In this case the worker thread doesn't acutally do anything so the program is effectively measuring the method of sychronization and context switching speed.

Can anyone tell me if there is something obviously inefficient about the C# code produced by the translation utility or is sychronization and context switching an achilles heel of C#?

namespace threadtest
{
using System;

public class ThreadTest
{

internal const bool DEBUG = false;

internal const int DEFAULT_THREAD_COUNT = 100;
internal const long DEFAULT_SWITCH_COUNT = 10;

internal static int threadCounter = 0;
internal static int threadCount = DEFAULT_THREAD_COUNT;
internal static long switches = DEFAULT_SWITCH_COUNT;
internal static System.Threading.Thread executiveThread = null;

[STAThread]
public static void Main(System.String[] args)
{

executiveThread = System.Threading.Thread.CurrentThread;

// *** get command line inputs
try
{
if (args.Length > 0)
{
threadCount = System.Int32.Parse(args[0]);
}

if (args.Length > 1)
{
switches = System.Int64.Parse(args[1]);
}
catch (System.FormatException nfe)
{
System.Console.Error.WriteLine("\nError parsing command
line parameters: " + args[0] + " " + args[1]);
}

// *** spawn threads
System.Threading.Thread[] threadPool;
threadPool = new System.Threading.Thread[threadCount];
try
{
for (int i = 1; i <= threadCount; ++i)
{
System.Console.Out.WriteLine(&quot;Creating Thread &quot; + i);
threadPool[i - 1] = new System.Threading.Thread(new
System.Threading.ThreadStart(createWorker().Run));
threadPool[i - 1].Start();
}
}
catch (System.OutOfMemoryException oome)
{
System.Console.Error.WriteLine(&quot;Out of Memory.&quot;);
System.Environment.Exit(0);
}

// ** Have executive wait for worker threads to catch up (quick and dirty).
try
{
System.Threading.Thread.Sleep(new System.TimeSpan(10000 * 5000));
}
catch (System.Threading.ThreadInterruptedException ie)
{
System.Console.Error.WriteLine(&quot;Executive thread interrupted.&quot;);
System.Environment.Exit(0);
}

// *** start indexing through worker threads

System.Console.Error.WriteLine(&quot;Starting test.&quot;);
long startTimeMillis = (System.DateTime.Now.Ticks - 621355968000000000) / 10000;

for (int i = 1; i <= switches; ++i)
{
System.Threading.Thread currentEntity = threadPool[(i - 1) % threadCount];

// *** must have entity lock to notify

lock(currentEntity)
{
if (DEBUG)
{
System.Console.Error.WriteLine(&quot;Exec has acquired entity thread &quot; + currentEntity.Name + &quot; lock.&quot;);
System.Console.Error.WriteLine(&quot;About to notify entity thread &quot; + currentEntity.Name + &quot;.&quot;);
}
// *** notify worker thread
System.Threading.Monitor.Pulse(currentEntity);

try
{
if (DEBUG)
{
System.Console.Error.WriteLine(&quot;Exec about to wait for notification.&quot;);
}
// *** wait for worker to finish
System.Threading.Monitor.Wait(currentEntity);
if (DEBUG)
{
System.Console.Error.WriteLine(&quot;Exec after being notified.&quot;);
}
}
catch (System.Threading.ThreadInterruptedException ie)
{
System.Console.Error.WriteLine(&quot;\nExecutive Thread was Interrupted!!!&quot;);
System.Environment.Exit(0);
}

} // end lock block

System.Threading.Monitor.Exit( currentEntity );

} // end for

// *** compute stats
long endTimeMillis = (System.DateTime.Now.Ticks - 621355968000000000) / 10000;
long deltaMillis = endTimeMillis - startTimeMillis;
double rate = (double) switches / (deltaMillis / 1000.0);
System.Console.Error.WriteLine(switches + &quot; context switches on &quot; + threadCount + &quot; threads
performed at &quot; + rate + &quot;/sec.&quot;);
System.Console.Error.WriteLine(&quot;Executive all done.&quot;);
System.Environment.Exit(0);

} // end Main( )


internal static Worker createWorker()
{
++threadCounter;
return new Worker( null, &quot;Entity_Thread-&quot; + threadCounter);
}

} // end ThreadTest

// *** Here's the worker thread

public class Worker : SupportClass.IThreadRunnable
{
public virtual System.String Name
{
get
{
return name;
}

set
{
name = value;
}

}

internal const bool DEBUG = false;
private System.String name;

public Worker( SupportClass.IThreadRunnable target, System.String nam)
{
Name = nam;
}

public void Run()
{
System.Threading.Thread myThread = System.Threading.Thread.CurrentThread;

lock(myThread)
{
while (true)
{
try
{
if (DEBUG)
{
System.Console.Error.WriteLine(&quot;Entity thread &quot; + Name + &quot; going on hold.&quot;);
}

// *** wait to be notified by the executive
System.Threading.Monitor.Wait(myThread);

if (DEBUG)
{
System.Console.Error.WriteLine(&quot;Entity thread &quot; + Name + &quot; off hold.&quot;);
}

}
catch (System.Threading.ThreadInterruptedException ie)
{
System.Console.Error.WriteLine(&quot;\nEntity Thread was Interrupted!!!&quot;);
}

// *** work to be done (if any) goes here

if (DEBUG)
{
System.Console.Error.WriteLine(&quot;Entity thread &quot; + Name + &quot; doing work.&quot;);
}

// notify the executive that I'm done
System.Threading.Monitor.Pulse(myThread);

if (DEBUG)
{
System.Console.Error.WriteLine(&quot;Notified executive.&quot;);
}

} // end while

} // end synchronized

} // end Run( )

} // end Worker

public class SupportClass
{
public interface IThreadRunnable
{
void Run();
}
}

}
 
> [STAThread]
public static void Main(System.String[] args) <

Is your application a Windows Forms app? If so, there may be additional stuff linked in that is slowing you down. Try changing the application type to a Console app (in your project/solution properties) and recompile. You may have to fix up some things like MessageBox (like Java alerts) that aren't supported in console apps, but try it that way.

Chip H.
 
The program is a console application with no gui (or other) adornments.
 
The [STAThread] attribute threw me off. You usually only see it in a Windows Forms application.

I'm sorry - but I don't know enough to help you.

Chip H.
 
You may be able to adjust the performance settings by going to My Computer/Properties/Performance (or slight variation of location dependent upon your OS). Dependent upon the OS, you can adjust performance for application or background performance along with a plethora of other settings. Neil Konitzer
Freisoft
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top