Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use ReadOnlySpan<char> to replace the 'new string(...)' to reduce memory allocation #3077

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

xuzhg
Copy link
Member

@xuzhg xuzhg commented Sep 30, 2024

Use ExpressionLexer to do ReadOnlySpan investigation and seek feebacks to move forward to all other ODL parts.

Be noted,

  1. the ReadOnlySpan doesn't override the operator == and != for string. Remember to use 'Equals(...)' to replace them.
ReadOnlySpan<char> s1 = "nav/$ref".AsSpan().Slice(4,4);

Console.WriteLine(s1.ToString());
Console.WriteLine(s1 == "$ref");
Console.WriteLine(s1 != "$ref");
Console.WriteLine(s1.Equals("$ref", StringComparison.Ordinal));

It outputs

$ref
False
True
True
  1. Anytime to call 'ToString()' on ReadOnlySpan will do the 'new string(...)', so be noted, not to call it multiple times until you need the string and remember to cache it.

Benchmark:

image

See the allocated, using 'ReadOnlySpan' is 1/3 memory allocation comparing to the 'new string(...)'.

Codes

   int NumberOfItems = 100000;
   public string expression = "abc(2014-09-19T12:13:14+00:00)/3258.678765765489753678965390/SRID=1234(POINT(10 20))/Function(foo=@x,bar=1,baz=@y)";

    [Benchmark]
    public void ExpressionLexerUsingNewStringSingleRun()
    {
        ExpressionLexer2 lexer = new Microsoft.OData.UriParser.ExpressionLexer2(expression, true, false);
        while (lexer.CurrentToken.Kind != ExpressionTokenKind.End)
        {
            lexer.NextToken();
        }
    }

    [Benchmark]
    public void ExpressionLexerUsingReadOnlySingleRun()
    {

        ExpressionLexer lexer = new Microsoft.OData.UriParser.ExpressionLexer(expression, true, false);
        while (lexer.CurrentToken.Kind != ExpressionTokenKind.End)
        {
            lexer.NextToken();
        }
    }

    [Benchmark]
    public void ExpressionLexerUsingNewString()
    {
        for (int i = 0; i < NumberOfItems; i++)
        {
            ExpressionLexer2 lexer = new Microsoft.OData.UriParser.ExpressionLexer2(expression, true, false);
            while (lexer.CurrentToken.Kind != ExpressionTokenKind.End)
            {
                lexer.NextToken();
            }
        }
    }

    [Benchmark]
    public void ExpressionLexerUsingReadOnly()
    {
        for (int i = 0; i < NumberOfItems; i++)
        {
            ExpressionLexer lexer = new Microsoft.OData.UriParser.ExpressionLexer(expression, true, false);
            while (lexer.CurrentToken.Kind != ExpressionTokenKind.End)
            {
                lexer.NextToken();
            }
        }
    }

Issues

This pull request fixes #xxx.

Description

Briefly describe the changes of this pull request.

Checklist (Uncheck if it is not completed)

  • Test cases added
  • Build and test with one-click build and test script passed

Additional work necessary

If documentation update is needed, please add "Docs Needed" label to the issue and provide details about the required document change in the issue.

…e the memory allocation during parsing.

Be noted, the ReadOnlySpan<char> doesn't override the operator == and != for string. Remember to use 'Equals(...)' to replace them.

Anytime to call 'ToString()' on ReadOnlySpan<char> will do the 'new string(...)', so be noted, not to call it mulitple times
@habbes
Copy link
Contributor

habbes commented Oct 15, 2024

@xuzhg could you share the benchmarks that you used for the perf tests, did you also try to run the existing UriParser benchmarks before and after these changes?

@xuzhg
Copy link
Member Author

xuzhg commented Oct 15, 2024

@xuzhg could you share the benchmarks that you used for the perf tests, did you also try to run the existing UriParser benchmarks before and after these changes?

I create a branch to hold the perf test at: https://github.com/OData/odata.net/tree/ReadOnlySpanBenchMark

This PR is just a starting point. We will move to UriParser and others in the comming step.

@xuzhg
Copy link
Member Author

xuzhg commented Oct 17, 2024

@habbes Resolved the comment. Please take a look again. I will keep moving to next step. It could be 'UriParser'.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants