Skip to main content
3 of 6
Ternary Operator vs Array indexing
PTwr
  • 526
  • 4
  • 6

TLDR: Get DirectBitmap from https://stackoverflow.com/a/34801225 and add

        public void SetPixel(int x, int y, int argb)
        {
            int index = x + (y * Width);

            Bits[index] = argb;
        }

then change GetStaticNoise to

        readonly static int white = Color.White.ToArgb();
        readonly static int black = Color.Black.ToArgb();
        readonly static Random NoiseRnd = new Random();
        private static DirectBitmap GetStaticNoise(int width, int height)
        {
            var bitmap = new DirectBitmap(width, height);
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    var rnd = NoiseRnd.Next(0, 2);

                    bitmap.SetPixel(x, y, rnd % 2 == 0 ? white : black);
                }
            }

            return bitmap;
        }

Long story: I have done stupid things in the past and know how to optimize your code

To start with, there is no need to guess when you can just ask code profiler to tell why your code is slow :)

Roughly 96% of CPU time is spent in GetStaticNoise in which major culprit is, unsurprisingly, SetPixel. code profile of GetStaticNoise

Before dealing with big fat pixel setting, lets take a look at other lines with surprisingly high CPU time.

  • bitmap.Width and bitmap.Height does bunch of stuff under the hood.
  • preset colors isn't a simple integer, but a fat object.
  • Random.Shared creates Random object for each call. It is also thread-safe, which means there will be some performance cost hidden inside
  • Ternary conditional operator generates different bytecode than full IF, and can be faster for conditional assignments

Few seconds of refactor later you get about +10% performance for "free". itsy bitsy refactor

You could optimize further by reusing Bitmap, but for that size would have to be either constant or hook into OnSizeChanged event.

Knowing that SetPixel is a performance hog, you can just search for "c# fast SetPixel" and just copy&paste ready to use solution from another SE post. Make sure to read through the comments on why it is not a perfect solution.

Lo and behold, performance increased tenfold just by not using SetPixel unnecessary safety logic.

But that ain't the end, let's run profiler again to get reminded that Color object sucks for game development.

ugh this damn Color object...

With color already being prefetched, we can preconvert it as well and suddenly code is so fast that property getter becomes performance hog, taking whole 5%! surprising performance hog

Chopping properties away you can reach spot where Random numbers takes longer to generate than SetPixel. diminishing returns

But at this point we have pretty much reached limits of software rendering on windows desktop. For faster performance you'll need to use GPU.

To clarify on using Ternary Operator as performance optimization: Two-element array is twice as fast as full If statement, and Ternary Operator is ~30% faster than array. Original code used modulo 2 to check parity, but with Random already returning only 0 or 1 it can be used without modulo for small performance gain. Ternary is cool Have in mind that (ab)using array indexes as logic (pointers) should work better in native code, without all the fancy memory management of .NET. Or it might not, depending on performance tricks done by CPU's themselves. Micro optimizations are tricky.

PTwr
  • 526
  • 4
  • 6