Am I using the wrong 'stackalloc' to cope with the string.Split() function to be used in Unity?

Viewed 208

I use Unity. but this engine cannot use Span .. so, I made string parsing function

My function purpose is converting string to Single struct value(int, float, bool, UnityEngine.Vector4 etc..) and i thought about how to not generate GC as much as possible

string data is read xml file Examples of string data specifications are: "-1234.23,234,-.232.344", "554", "-.55"

During the test, I found something interesting

    public unsafe Vector4 GetVectorToStackAlloc()
    {
        Vector4 vec = Vector4.Zero;

        char* data = stackalloc char[8];
        int dot = -1;
        int index = 0;
        int colume = 0;
        for (int i = 0; i < Str.Length; ++i)
        {
            if (Str[i] == Token)
            {
                Parse();

                dot = -1;
                index = 0;
                colume++;
            }
            else
            {
                if (Str[i] == '.') dot = index;
                data[index++] = Str[i];
            }
        }

        Parse();

        void Parse()
        {
            if (index == 0) return;

            bool isMinus = data[0] == '-';
            int length = isMinus ? 1 : 0;
            int mul = dot != -1 ? (dot - index) + 1 : 0;

            for (int x = index - 1; x >= length; --x)
            {
                if (data[x] == '.') continue;

                int k = data[x] - '0';
                float m = MathF.Pow(10, mul++);
                vec[colume] += k * m;
            }

            if (isMinus) vec[colume] *= -1;
        }

        return vec;
    }

    public Vector4 GetDefaultVector()
    {
        Vector4 vec = Vector4.Zero;

        string[] split = Str.Split(Token);
        for (int i = 0; i < split.Length; ++i)
        {
            if (string.IsNullOrWhiteSpace(split[i])) continue;
            vec[i] = float.Parse(split[i]);
        }

        return vec;
    }

    public Vector4 GetSpanVector()
    {
        Vector4 vec = Vector4.Zero;

        Span<char> span = stackalloc char[8];
        int index = 0;
        int colume = 0;
        for (int i = 0; i < Str.Length; ++i)
        {
            if (Str[i] == ',')
            {
                if (index == 0) { vec[colume++] = 0; }
                else vec[colume++] = float.Parse(span);

                index = 0;
                span.Clear();
            }
            else span[index++] = Str[i];
        }

        vec[colume] = float.Parse(span);
        return vec;
    }

Testing Vector4

During the testing process, I discovered that there was overhead in float.Parse() function and tested with a single float

public unsafe float GetStackAllocFloat()
    {
        float value = 0;
        char* data = stackalloc char[8];

        int dot = -1;
        int index = 0;
        for (int i = 0; i < Str.Length; ++i)
        {
            if (Str[i] == '.') dot = index;
            data[index++] = Str[i];
        }

        if (index == 0) return value;

        bool isMinus = data[0] == '-';
        int length = isMinus ? 1 : 0;
        int mul = dot != -1 ? (dot - index) + 1 : 0;

        for (int x = index - 1; x >= length; --x)
        {
            if (data[x] == '.') continue;

            int k = data[x] - '0';
            float m = MathF.Pow(10, mul++);
            value += k * m;
        }

        if (isMinus) value *= -1;
        return value;
    }

    public unsafe float GetDefaultFloat()
    {
        return float.Parse(Str);
    }

Testing Float

I think it is better to use stackalloc if the test method is normal. Had I made mistake?

1 Answers

OK, I tested it again to fit the Unity engine as much as possible

I think it's not bad!

enter image description here

public readonly string[] Strs = new string[200] { // data is fill .. }
public const char Token = ',';

    [Benchmark]
    public unsafe Vector4 GetVectorToStackAlloc()
    {
        Vector4 result = Vector4.Zero;

        int r = Strs.Length - 1;
        while (r != -1)
        {
            Vector4 vec = Vector4.Zero;

            char* data = stackalloc char[12];
            int dot = -1;
            int index = 0;
            int colume = 0;

            string Str = Strs[r--];
            for (int i = 0; i < Str.Length; ++i)
            {
                if (Str[i] == Token)
                {
                    vec[colume++] = Parse(index, data, dot);

                    dot = -1;
                    index = 0;
                }
                else
                {
                    if (Str[i] == '.') dot = index;
                    data[index++] = Str[i];
                }
            }

            vec[colume] = Parse(index, data, dot);
            result.x += vec.x;
        }
        return result;

        float Parse(int _index, char* _data, int _dot)
        {
            if (_index == 0) return 0;
            float val = 0;

            bool isMinus = _data[0] == '-';
            int length = isMinus ? 1 : 0;
            int mul = _dot != -1 ? (_dot - _index) + 1 : 0;

            for (int x = _index - 1; x >= length; --x)
            {
                if (_data[x] == '.') continue;

                int k = _data[x] - '0';
                float m = MathF.Pow(10, mul++);
                val = k * m;
            }

            return isMinus ? val * -1 : val;
        }
    }

    [Benchmark]
    public Vector4 GetDefaultVector()
    {
        Vector4 result = Vector4.Zero;

        int r = Strs.Length - 1;
        while (r != -1)
        {
            Vector4 vec = Vector4.Zero;
            string Str = Strs[r--];
            string[] split = Str.Split(Token);
            for (int i = 0; i < split.Length; ++i)
            {
                if (string.IsNullOrWhiteSpace(split[i])) continue;
                vec[i] = float.Parse(split[i]);
            }

            result.x += vec.x;
        }

        return result;
    }
Related