#define PERF_FALSEX
#define PERF_TRUEX
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using ZedGraph;
namespace FalseSharingDemo
{
public partial class Form1 : Form
{
SynchronizationContext _Sync = null;
bool _IsRunning = false;
CancellationTokenSource _CTS = new CancellationTokenSource();
PointPairList _nn = new PointPairList();
PointPairList _ny = new PointPairList();
PointPairList _yn = new PointPairList();
PointPairList _yy = new PointPairList();
public Form1()
{
InitializeComponent();
Shown += new EventHandler( HandlesShown );
_Sync = SynchronizationContext.Current;
zg.GraphPane.Title.IsVisible = false;
zg.GraphPane.XAxis.Title.Text = "Thread Count";
ShowResults();
rbSpeedup.CheckedChanged += ( s, e ) => ShowResults();
}
void HandlesShown( object sender, EventArgs e )
{
#if PERF_FALSE
for ( int i = 0 ; i < 9 ; i++ ) Work( Environment.ProcessorCount, false ); Close();
#elif PERF_TRUE
for ( int i = 0 ; i < 9 ; i++ ) Work( Environment.ProcessorCount, false, padding: true, spacing: true ); Close();
#endif
}
void btnTest_Click( object sender, EventArgs e )
{
StartStop();
}
class ToggleResults
{
public bool IsRunning { get; private set; }
public CancellationTokenSource CTS { get; private set; }
public ToggleResults( bool isRunning, CancellationTokenSource cts = null )
{
IsRunning = isRunning;
CTS = cts;
}
}
ToggleResults ToggleRunning( CancellationTokenSource cts )
{
Debug.Assert( SynchronizationContext.Current == _Sync, "ToggleRunning must be called on the UI thread" );
if ( cts != _CTS ) return new ToggleResults( false );
if ( _IsRunning )
{
btnTest.Text = "Start";
_IsRunning = false;
_CTS.Cancel();
_CTS = null;
}
else
{
btnTest.Text = "Cancel";
_IsRunning = true;
_CTS = new CancellationTokenSource();
}
return new ToggleResults( _IsRunning, _CTS );
}
void StartStop()
{
var toggle = ToggleRunning( _CTS );
if ( !toggle.IsRunning ) return;
var cts = toggle.CTS;
int REPEAT = tbAccuracy.Value;
bool oneWriter = cbOneWriter.Checked;
pb.Value = 0;
var nn = new PointPairList();
var ny = new PointPairList();
var yn = new PointPairList();
var yy = new PointPairList();
var task = Task.Factory.StartNew( () =>
{
Trace.WriteLine( "\n\nRun: " + DateTime.Now.TimeOfDay + "\n" );
Work( Environment.ProcessorCount, true, padding: true, spacing: true ); // JIT
int max = Environment.ProcessorCount * 4;
int cur = 0;
for ( int threads = 1 ; threads <= Environment.ProcessorCount ; threads++ )
{
nn.Add( threads, Enumerable.Range( 0, REPEAT ).Median( i => Work( threads, oneWriter ) ) );
if ( CheckProgress( ++cur, max, cts.Token ) ) return;
ny.Add( threads, Enumerable.Range( 0, REPEAT ).Median( i => Work( threads, oneWriter, spacing: true ) ) );
if ( CheckProgress( ++cur, max, cts.Token ) ) return;
yn.Add( threads, Enumerable.Range( 0, REPEAT ).Median( i => Work( threads, oneWriter, padding: true ) ) );
if ( CheckProgress( ++cur, max, cts.Token ) ) return;
yy.Add( threads, Enumerable.Range( 0, REPEAT ).Median( i => Work( threads, oneWriter, padding: true, spacing: true ) ) );
if ( CheckProgress( ++cur, max, cts.Token ) ) return;
}
} );
task.ContinueWith( prev =>
{
_nn = nn; _ny = ny;
_yn = yn; _yy = yy;
ToggleRunning( cts );
ShowResults();
},
TaskScheduler.FromCurrentSynchronizationContext() ); // ui thread
}
bool CheckProgress( int cur, int max, CancellationToken ct )
{
if ( ct.IsCancellationRequested ) return true;
_Sync.Send( o => pb.Value = 100 * cur / max, null );
return false;
}
void ShowResults()
{
bool speedup = rbSpeedup.Checked;
zg.GraphPane.YAxis.Title.Text = speedup ? "Speedup" : "Efficiency ( % )";
var nn = speedup ? _nn : new PointPairList( _nn.Select( p => p.X ).ToArray(), _nn.Select( p => 100d * p.Y / p.X ).ToArray() );
var ny = speedup ? _ny : new PointPairList( _ny.Select( p => p.X ).ToArray(), _ny.Select( p => 100d * p.Y / p.X ).ToArray() );
var yn = speedup ? _yn : new PointPairList( _yn.Select( p => p.X ).ToArray(), _yn.Select( p => 100d * p.Y / p.X ).ToArray() );
var yy = speedup ? _yy : new PointPairList( _yy.Select( p => p.X ).ToArray(), _yy.Select( p => 100d * p.Y / p.X ).ToArray() );
zg.GraphPane.CurveList.Clear();
zg.GraphPane.AddCurve( "No padding, no spacing", nn, Color.Red, SymbolType.Circle );
zg.GraphPane.AddCurve( "No padding, spacing", ny, Color.BlueViolet, SymbolType.Diamond );
zg.GraphPane.AddCurve( "Padding, no spacing", yn, Color.Blue, SymbolType.Square );
zg.GraphPane.AddCurve( "Padding, spacing", yy, Color.LimeGreen, SymbolType.Star );
zg.RestoreScale( zg.GraphPane );
}
double Work( int threadCount, bool oneWriter, bool padding = false, bool spacing = false, int affinity = -1 )
{
int iPadding = padding ? 32 : 0;
int iSpacing = spacing ? 32 : 1;
var trace = String.Empty;
trace += "ThreadCount: " + threadCount;
trace += " - Padding: " + iPadding;
trace += " - Spacing: " + iSpacing;
if ( affinity != -1 ) trace += " - Affinity: " + affinity;
Trace.WriteLine( trace );
if ( affinity == -1 ) affinity = 1;
var sequential = Task.Factory.StartNew<TimeSpan>( new Worker( 1, oneWriter, iPadding, iSpacing, affinity ).Work );
var parallel = sequential.ContinueWith<TimeSpan>( prev => new Worker( threadCount, oneWriter, iPadding, iSpacing, affinity ).Work() );
double y = 0d;
var results = parallel.ContinueWith( prev =>
{
var speedup = sequential.Result.TotalSeconds / parallel.Result.TotalSeconds;
var slowdown = 1d / speedup;
var efficiency = 100d * speedup / threadCount;
Trace.WriteLine(
"Speedup: " + speedup.ToString( "N2" ) +
" ; Slowdown: " + slowdown.ToString( "N2" ) +
" ; Efficiency: " + efficiency.ToString( "N2" ) + "%\n" );
y = speedup;
} );
results.Wait();
return y;
}
}
}