Skip to content

Significant performance issue when Parser.GetDefault() is called repeatedly #73

@codinglifestyle

Description

@codinglifestyle

I’ve encountered a significant performance issue with UAParser when it’s used in a logger. The issue arises when Parser.GetDefault() is called repeatedly during large operations. Each call to the logger creates a new instance of the Parser, which leads to a significant performance penalty.

Here’s a simplified test case that demonstrates the issue:

C#

using System;
using System.Diagnostics;
using UAParser;

class Program
{
    const int NumTests = 100;

    static void Main()
    {
        string userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3";

        var stopwatch = Stopwatch.StartNew();
        for (int i = 0; i < NumTests; i++)
        {
            var parser = Parser.GetDefault();
            var clientInfo = parser.Parse(userAgent);
        }
        stopwatch.Stop();
        Console.WriteLine($"Test Case 1 elapsed time: {stopwatch.ElapsedMilliseconds} ms");

        stopwatch.Restart();
        var parserOutsideLoop = Parser.GetDefault();
        for (int i = 0; i < NumTests; i++)
        {
            var clientInfo = parserOutsideLoop.Parse(userAgent);
        }
        stopwatch.Stop();
        Console.WriteLine($"Test Case 2 elapsed time: {stopwatch.ElapsedMilliseconds} ms");
    }
}

In Test Case 1, Parser.GetDefault() is called 100 times, leading to a total elapsed time of 4046 ms. In Test Case 2, Parser.GetDefault() is called only once, and the same instance is used for all iterations of the loop, resulting in a total elapsed time of 190 ms.

Test Case 1 elapsed time: 4046 ms
Test Case 2 elapsed time: 190 ms

This demonstrates that the time taken to instantiate the Parser object with Parser.GetDefault() is quite significant. It seems that the regex patterns are loaded from the assembly each time Parser.GetDefault() is called.

The actual fix was to instantiate the Parser as a member variable of the logger class, so it’s created only once and reused for all subsequent calls to the logger.

I couldn’t find any explicit warnings or notes about this in the UAParser documentation. It would be helpful if the documentation could provide some guidance or warning about this so others don't repeat the same bug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions