Click here to Skip to main content
15,901,122 members
Articles / Programming Languages / C#
Tip/Trick

Sequential Structure Layout For Speed

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
21 Jul 2010CPOL1 min read 14.9K   2   5
This code will Highlight Wise Choice for sequential layout Structure
We are going to declare a Matrix which is used to house a Set.. Explicitly.  Just by putting the iteration value in the middle of the sequence you can speed up that example code by a factor of 36% !.. and that is just by adjusting the location of data in the memory....

The code below highlights the differences (there was a nice table here to show the differences... but it has been improved to be actual code (not it's intent, so I edited it out :( ) Nearly Full code is given below.


The thing to notice is that position of the class member i is in different places.. the reason this is so useful is that it highlights quite nicely the effect of cache misses... a quick look at the Compiler choice classes will show you where the differences are...

And Here are the results from adding i to the matrix elements... (this was individually timed inside of a for loop  200 times, then a simple average was taken)... Total savings 0.0175 seconds! This was pitted against the CLR and CLR results are somewhere in between (as would be expected).

--Upgraded From My Blog.... :laugh:


Here is the Running code, The CompilerChoice classes are CLR layout comparisons (results not shown)



[StructLayout(LayoutKind.Explicit)]
public class PoorChoice
{
    [FieldOffset(0)]
    public int i;
    [FieldOffset(4)]
    public int m00;
    [FieldOffset(8)]
    public int m01;
    [FieldOffset(16)]
    public int m02;
    [FieldOffset(20)]
    public int m03;
    [FieldOffset(24)]
    public int m10;
    [FieldOffset(28)]
    public int m11;
    [FieldOffset(32)]
    public int m12;
    [FieldOffset(36)]
    public int m13;
    [FieldOffset(40)]
    public int m20;
    [FieldOffset(44)]
    public int m21;
    [FieldOffset(48)]
    public int m22;
    [FieldOffset(52)]
    public int m23;
    [FieldOffset(56)]
    public int m30;
    [FieldOffset(60)]
    public int m31;
    [FieldOffset(64)]
    public int m32;
    [FieldOffset(68)]
    public int m33;
}

[StructLayout(LayoutKind.Explicit)]
public class GreatChoice
{
    [FieldOffset(0)]
    public int m00;
    [FieldOffset(4)]
    public int m01;
    [FieldOffset(8)]
    public int m02;
    [FieldOffset(12)]
    public int m03;
    [FieldOffset(16)]
    public int m10;
    [FieldOffset(20)]
    public int m11;
    [FieldOffset(24)]
    public int m12;
    [FieldOffset(28)]
    public int m13;
    [FieldOffset(32)]
    public byte i;
    [FieldOffset(33)]
    public int m20;
    [FieldOffset(37)]
    public int m21;
    [FieldOffset(41)]
    public int m22;
    [FieldOffset(45)]
    public int m23;
    [FieldOffset(49)]
    public int m30;
    [FieldOffset(53)]
    public int m31;
    [FieldOffset(57)]
    public int m32;
    [FieldOffset(61)]
    public int m33;
}

public class CompilerChoice1
{
    public int i;
    public int m00;
    public int m01;
    public int m02;
    public int m03;
    public int m10;
    public int m11;
    public int m12;
    public int m13;
    public int m20;
    public int m21;
    public int m22;
    public int m23;
    public int m30;
    public int m31;
    public int m32;
    public int m33;
}

public class CompilerChoice2
{
    public int m00;
    public int m01;
    public int m02;
    public int m03;
    public int m10;
    public int m11;
    public int m12;
    public int m13;
    public byte i;
    public int m20;
    public int m21;
    public int m22;
    public int m23;
    public int m30;
    public int m31;
    public int m32;
    public int m33;
}
    public GreatLayoutTest(System.Windows.Forms.Label label)
    {
        Console.WriteLine("the Size of Poor Choice is:" + System.Runtime.InteropServices.Marshal.SizeOf(typeof(PoorChoice)).ToString());
        Console.WriteLine("the Size of Great Choice is:" + System.Runtime.InteropServices.Marshal.SizeOf(typeof(GreatChoice)).ToString());
        //Type 'ThreadingBestPractices.CompilerChoice1' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed."
        //Console.WriteLine("the Size of Compiler Choice 1 is:" + System.Runtime.InteropServices.Marshal.SizeOf(typeof(CompilerChoice1)).ToString());
        //Type 'ThreadingBestPractices.CompilerChoice1' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed."
        //Console.WriteLine("the Size of Compiler Choice 2 is:" + System.Runtime.InteropServices.Marshal.SizeOf(typeof(CompilerChoice2)).ToString());
        List<double[]> ControlResults = new List<double[]>();
        PoorLayout pl = new PoorLayout();
        label.Text = pl.Test();
        TimeSpan[] ts = new TimeSpan[2];
        DateTime[] ts_start = new DateTime[2];
        double[] duration = new double[4];

        PoorChoice poor = new PoorChoice();
        GreatChoice great = new GreatChoice();
        CompilerChoice1 clr1 = new CompilerChoice1();
        CompilerChoice2 clr2 = new CompilerChoice2();

        for (int i = 0; i < 200; i++)
        {
            label.Text = (200 - i).ToString(); label.Refresh();
            #region Poor
            Stopwatch sw = Stopwatch.StartNew();
            poor.m00 += poor.i;
            poor.i++;
            poor.m01 += poor.i;
            poor.i++;
            poor.m02 += poor.i;
            poor.i++;
            poor.m03 += poor.i;
            poor.i++;
            poor.m10 += poor.i;
            poor.i++;
            poor.m11 += poor.i;
            poor.i++;
            poor.m12 += poor.i;
            poor.i++;
            poor.m13 += poor.i;
            poor.i++;
            poor.m20 += poor.i;
            poor.i++;
            poor.m21 += poor.i;
            poor.i++;
            poor.m22 += poor.i;
            poor.i++;
            poor.m23 += poor.i;
            poor.i++;
            poor.m30 += poor.i;
            poor.i++;
            poor.m31 += poor.i;
            poor.i++;
            poor.m32 += poor.i;
            poor.i++;
            poor.m33 += poor.i;
            poor.i++;
            duration[0] = sw.Elapsed.TotalMilliseconds;
            #endregion

            #region great
            sw = Stopwatch.StartNew();
            great.m00 += great.i;
            great.i++;
            great.m01 += great.i;
            great.i++;
            great.m02 += great.i;
            great.i++;
            great.m03 += great.i;
            great.i++;
            great.m10 += great.i;
            great.i++;
            great.m11 += great.i;
            great.i++;
            great.m12 += great.i;
            great.i++;
            great.m13 += great.i;
            great.i++;
            great.m20 += great.i;
            great.i++;
            great.m21 += great.i;
            great.i++;
            great.m22 += great.i;
            great.i++;
            great.m23 += great.i;
            great.i++;
            great.m30 += great.i;
            great.i++;
            great.m31 += great.i;
            great.i++;
            great.m32 += great.i;
            great.i++;
            great.m33 += great.i;
            great.i++;
            duration[1] = sw.Elapsed.TotalMilliseconds;
            #endregion

            #region Compiler1
            sw = Stopwatch.StartNew();
            clr1.m00 += clr1.i;
            clr1.i++;
            clr1.m01 += clr1.i;
            clr1.i++;
            clr1.m02 += clr1.i;
            clr1.i++;
            clr1.m03 += clr1.i;
            clr1.i++;
            clr1.m10 += clr1.i;
            clr1.i++;
            clr1.m11 += clr1.i;
            clr1.i++;
            clr1.m12 += clr1.i;
            clr1.i++;
            clr1.m13 += clr1.i;
            clr1.i++;
            clr1.m20 += clr1.i;
            clr1.i++;
            clr1.m21 += clr1.i;
            clr1.i++;
            clr1.m22 += clr1.i;
            clr1.i++;
            clr1.m23 += clr1.i;
            clr1.i++;
            clr1.m30 += clr1.i;
            clr1.i++;
            clr1.m31 += clr1.i;
            clr1.i++;
            clr1.m32 += clr1.i;
            clr1.i++;
            clr1.m33 += clr1.i;
            clr1.i++;
            duration[2] = sw.Elapsed.TotalMilliseconds;
            #endregion

            #region Compiler2
            sw = Stopwatch.StartNew();
            clr2.m00 += clr2.i;
            clr2.i++;
            clr2.m01 += clr2.i;
            clr2.i++;
            clr2.m02 += clr2.i;
            clr2.i++;
            clr2.m03 += clr2.i;
            clr2.i++;
            clr2.m10 += clr2.i;
            clr2.i++;
            clr2.m11 += clr2.i;
            clr2.i++;
            clr2.m12 += clr2.i;
            clr2.i++;
            clr2.m13 += clr2.i;
            clr2.i++;
            clr2.m20 += clr2.i;
            clr2.i++;
            clr2.m21 += clr2.i;
            clr2.i++;
            clr2.m22 += clr2.i;
            clr2.i++;
            clr2.m23 += clr2.i;
            clr2.i++;
            clr2.m30 += clr2.i;
            clr2.i++;
            clr2.m31 += clr2.i;
            clr2.i++;
            clr2.m32 += clr2.i;
            clr2.i++;
            clr2.m33 += clr2.i;
            clr2.i++;
            duration[3] = sw.Elapsed.TotalMilliseconds;
            #endregion
            results.Add(new double[] {
                duration[0],
                duration[1],
                duration[2],
                duration[3]});
        }
        IO.WriteOut("GreatLayoutTest.txt", results);
        label.Text = "Done";
    }

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Product Manager Magenic Technologies
United States United States
I'm sometimes also active at the XNA Creaters Club Fourms.

I'm currently the leader of the Twin Cities XNA User Group.

Comments and Discussions

 
GeneralReason for my vote of 5 Learned a new thing today. Thanks fo... Pin
GPUToaster™21-Jul-10 19:27
GPUToaster™21-Jul-10 19:27 
GeneralCode sample added... Pin
ely_bob21-Jul-10 3:47
professionalely_bob21-Jul-10 3:47 
GeneralBenchmarks mean nothing without code. Please provide some. Pin
leppie20-Jul-10 20:18
leppie20-Jul-10 20:18 
Generalother processes affect results of test Pin
arcticbrew22-Jul-10 8:17
arcticbrew22-Jul-10 8:17 
GeneralRe: other processes affect results of test Pin
ely_bob22-Jul-10 14:56
professionalely_bob22-Jul-10 14:56 
arcticbrew wrote:
The tests you are doing will be affected by other processes and threads running on your computer, your own IO, and using a byte instead of an int.


This code doesn't time over any IO. it only times over the blocks of interest.

The stopwatch used was High Resolution. Tested By: MessageBox.Show("Current Stopwatch is "+(Stopwatch.IsHighResolution)?"High Resolution":"Low Resolution");

CPU performance and execution time are two different things,
a) there are many things that make a small toll on the cpu (perform well) yet take longer to do Math.ArcTan for example.
b) The amount of time that it takes to complete a given task is independent of what else is happening (assuming of course that I'm not running two applications which use a lot of the same chipset features).. i.e. no two programs doing serious geometric calculations at the same time. however even if they were this timer catches that and figures it into the average.

these timings are atomic, and fine grain, as well as fairly will pipelined for execution, and the order of blocks chosen very carefully. This is but one example of a much larger set of work which is available.

arcticbrew wrote:
These structures are really small why do you think they show the results of cache misses. Are you running them on an embedded system cpu with a 1KB cache ?



This was done on a machine with a L1 cache size of 32 (32bit machine).. you wouldn't see an appreciable difference if it was run on a 64 bit machine.... Laugh | :laugh:
Additionally If you want a more pronounced difference double or more number of the matrix (m) values so that your final values are larger structures and You should this same result keeping the iterator either at the head or center of the structure... .... Cool | :cool:
I'd blame it on the Brain farts.. But let's be honest, it really is more like a Methane factory between my ears some days then it is anything else...

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.