Search code examples
c#performanceoptimization

Is there any faster way to do this? (Optimization or just straight-up doing it a different way)


I'm trying to extract all elements of a 2D array and place them into a string, so far the code completes in an average of ~950ms but I was wondering if there's any faster way of doing this.

All the buffers are 256x256 char arrays.

for (int ii = 0; ii < 255; ii++)
{
    for (int i = 0; i < 255; i++)
    {
        Export = Export + BufferC[i, ii];
    }
}

The full context of the code snippet above. The ~950ms measurement is for the ExtractA() function, with the other buffers taking a reasonable amount of time, but optimization would also be appreciated.

public static string ExportBuffer(int Buffer, int Line, int Dimension = 2, int Start = 0, int End = 255)
{
    string Export = "Test";

    for (int ii = 0; ii < 255; ii++)
    {
        for (int i = 0; i < 255; i++)
        {
            BufferC[i, ii] = 'A';
        }
    }

    switch (Buffer)
    {
        default: throw new NotImplementedException("The buffer selected does not exist.");
        case 00:  ExtractDisplayBuffer(); break;
        case 01:        ExtractBufferA(); break;
        case 02:        ExtractBufferB(); break;
        case 03:        ExtractBufferC(); break;
    }
    void ExtractDisplayBuffer()
    {
        switch (Dimension)
        {
            case 0: ExtractX(); break;
            case 1: ExtractY(); break;
            case 2: ExtractA(); break;
        }
        void ExtractX()
        {
            for (int i = Start; i < End; i++)
            {
                Export = Export + BufferC[i, ii];
            }
        }
        void ExtractY()
        {
            for (int i = Start; i < End; i++)
            {
                Export = Export + BufferC[i, ii];
            }
        }
        void ExtractA()
        {
            for (int ii = 0; ii < 255; ii++)
            {
                for (int i = 0; i < 255; i++)
                {
                    Export = Export + BufferC[i, ii];
                }
            }
        }
    }
    void ExtractBufferA()
    {
        switch (Dimension)
        {
            case 0: ExtractX(); break;
            case 1: ExtractY(); break;
            case 2: ExtractA(); break;
        }
        void ExtractX()
        {
            for (int i = Start; i < End; i++)
            {
                Export = Export + BufferC[i, ii];
            }
        }
        void ExtractY()
        {
            for (int i = Start; i < End; i++)
            {
                Export = Export + BufferC[i, ii];
            }
        }
        void ExtractA()
        {
            for (int ii = 0; ii < 255; ii++)
            {
                for (int i = 0; i < 255; i++)
                {
                    Export = Export + BufferC[i, ii];
                }
            }
        }
    }
    void ExtractBufferB()
    {
        switch (Dimension)
        {
            case 0: ExtractX(); break;
            case 1: ExtractY(); break;
            case 2: ExtractA(); break;
        }
        void ExtractX()
        {
            for (int i = Start; i < End; i++)
            {
                Export = Export + BufferC[i, ii];
            }
        }
        void ExtractY()
        {
            for (int i = Start; i < End; i++)
            {
                Export = Export + BufferC[i, ii];
            }
        }
        void ExtractA()
        {
            for (int ii = 0; ii < 255; ii++)
            {
                for (int i = 0; i < 255; i++)
                {
                    Export = Export + BufferC[i, ii];
                }
            }
        }
    }
    void ExtractBufferC()
    {
        switch (Dimension)
        {
            case 0: ExtractX(); break;
            case 1: ExtractY(); break;
            case 2: ExtractA(); break;
        }
        void ExtractX()
        {
            for (int i = Start; i < End; i++)
            {
                Export = Export + BufferC[i, ii];
            }
        }
        void ExtractY()
        {
            for (int i = Start; i < End; i++)
            {
                Export = Export + BufferC[i, ii];
            }
        }
        void ExtractA()
        {
            for (int ii = 0; ii < 255; ii++)
            {
                for (int i = 0; i < 255; i++)
                {
                    Export = Export + BufferC[i, ii];
                }
            }
        }
    }
    return Export;
}

Solution

  • You can use MemoryMarshal.CreateSpan() to create a ReadOnlySpan<char> from a 2D char array, and then you can pass that span to the string constructor.

    I think this will be one of the more performant approaches:

    var buffer = new char[256, 256];
    
    for (int i = 0; i < 256; i++)
    {
        for (int j = 0; j < 256; j++) // Fill with some arbitrary daya.
        {
            buffer[i, j] = (char)('A' + (i * 256 + j) % 64);
        }
    }
    
    // Create the span.
    var span = MemoryMarshal.CreateSpan(
        ref Unsafe.As<byte, char>(ref MemoryMarshal.GetArrayDataReference(buffer)),
        buffer.Length);
    
    // Create the string from the span.
    string s = new string(span);
    
    // Show that the string contains the correct data.
    Console.WriteLine(s);
    

    Note that creating a span in the code above does NOT copy any data. However, the data will be copied once in the call to new string(span), but the implementation uses a very fast copy.

    You might like to write a separate method to convert 2D arrays of any type to make the code more readable:

    public static ReadOnlySpan<T> TwoDimensionalArrayAsSpan<T>(T[,] array)
    {
        return MemoryMarshal.CreateSpan(
            ref Unsafe.As<byte, T>(ref MemoryMarshal.GetArrayDataReference(array)),
            array.Length);
    }
    

    Then creating the span would look like:

    // Create the span.
    var span = TwoDimensionalArrayAsSpan(buffer);