question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

.net 4.7.2 ~10 times worse performance than .net core (or old v2 sdk with 4.7.2)

See original GitHub issue

Describe the bug On .net 4.7.2 we get horrible performance using the new v3 library. The same Linq query that matches all Documents where one varible is true takes 80-200ms with .net core and over a full second with .net, it also takes only around 150ms with the old v2 library using .net 4.7.2.

To Reproduce We have been using a Repository pattern approach with a generic static class.

Shrunken down Repository

public class CosmosDbRepository<T> where T : EntityBase
{

    private static CosmosClient client;
    private static Database database;
    private static Container container;

    private const string DEFAULT_PARTITION_KEY = "/_partitionKey";
    private const int DEFAULT_REQUEST_UNITS = 400;

    public static void Initialize(
        string collection = null,
        string partitionKeyPath = DEFAULT_PARTITION_KEY,
        int requestUnits = DEFAULT_REQUEST_UNITS,
        string endpoint = null,
        string authKey = null,
        string databaseName = null)
    {
        var options = new CosmosClientOptions()
        {
            ConnectionMode = ConnectionMode.Direct
        };

        client = new CosmosClient(
            endpoint ?? "<insert_endpoint>",
            authKey ?? "<insert_api_key>",
            clientOptions: options
        );

        database = client.CreateDatabaseIfNotExistsAsync(databaseName ?? "<insert_db>").Result;
        container = database.CreateContainerIfNotExistsAsync(collection ?? typeof(T).Name, partitionKeyPath, requestUnits).Result;
    }

    public static async Task<IEnumerable<T>> QueryAsync(
        Expression<Func<T, bool>> whereExpr,
        Expression<Func<T, object>> orderExpr = null,
        SortOrder sortOrder = SortOrder.Descending,
        PartitionKey? partitionKey = null,
        int? take = null)
    {
        var iterator = ConstructQueryable(
            whereExpr: whereExpr,
            orderExpr: orderExpr,
            sortOrder: sortOrder,
            partitionKey: partitionKey,
            take: take
            ).ToFeedIterator();

        var results = new List<T>();

        while (iterator.HasMoreResults)
        {
            var result = await iterator.ReadNextAsync();
            Console.WriteLine($"Result count: {result.Count} Used up: {result.RequestCharge} RUs");
            results.AddRange(result.Resource);
        }

        return results;
    }

    private static IQueryable<T> ConstructQueryable(
        Expression<Func<T, bool>> whereExpr = null,
        Expression<Func<T, object>> orderExpr = null,
        int? maxItemCount = null,
        int? take = null,
        string continuationToken = null,
        SortOrder sortOrder = SortOrder.Descending,
        PartitionKey? partitionKey = null)
    {
        var options = new QueryRequestOptions()
        {
            PartitionKey = partitionKey,
            MaxItemCount = maxItemCount,
            MaxConcurrency = -1,
            MaxBufferedItemCount = -1,
            EnableScanInQuery = true
        };

        IQueryable<T> queryable = container.GetItemLinqQueryable<T>(
            requestOptions: options,
            continuationToken: continuationToken,
            allowSynchronousQueryExecution: true);

        if (orderExpr != null)
        {
            if (sortOrder == SortOrder.Descending)
                queryable = queryable.OrderByDescending(orderExpr);
            else
                queryable = queryable.OrderBy(orderExpr);
        }

        if (whereExpr != null) queryable = queryable.Where(whereExpr);
        if (take != null) queryable = queryable.Take(take.Value);

        return queryable;
    }
}

Base class for DocumentDB Entities/Documents.

    public abstract class EntityBase
    {
        [JsonProperty(PropertyName = "id")]
        public string DocumentId { get; set; }

        [JsonIgnore]
        public virtual PartitionKey PartitionKey { get { return PartitionKey.None; } }
    }

Test Console Application

        static void Main(string[] args)
        {
            CosmosDbRepository<SomeClassThatDerivesEntityBase>.Initialize();

            var results = CosmosDbRepository<SomeClassThatDerivesEntityBase>.QueryAsync(
                whereExpr: x => x.ActiveDie).Result;

            var sp = new Stopwatch();

            for (int i = 0; i < 10; i++)
            {
                sp.Start();
                results = CosmosDbRepository<SomeClassThatDerivesEntityBase>.QueryAsync(
                   whereExpr: x => x.SomeBooleanProperty).Result;
                sp.Stop();
                Console.WriteLine($"This took {sp.ElapsedMilliseconds}ms.");
                sp.Reset();
            }

            Console.ReadLine();
        }

I can´t provide you with the classes I used or the documents. But it is a relatively big class with 27 properties and 2 of them being other classes.

Output of that Console Application

.net core

Result count: 65 Used up: 9,05 RUs
Result count: 65 Used up: 9,05 RUs
This took 284ms.
Result count: 65 Used up: 9,05 RUs
This took 276ms.
Result count: 65 Used up: 9,05 RUs
This took 92ms.
Result count: 65 Used up: 9,05 RUs
This took 88ms.
Result count: 65 Used up: 9,05 RUs
This took 81ms.
Result count: 65 Used up: 9,05 RUs
This took 285ms.
Result count: 65 Used up: 9,05 RUs
This took 78ms.
Result count: 65 Used up: 9,05 RUs
This took 83ms.
Result count: 65 Used up: 9,05 RUs
This took 85ms.
Result count: 65 Used up: 9,05 RUs
This took 78ms.

.net 4.7.2

Result count: 65 Used up: 9,05 RUs
Result count: 65 Used up: 9,05 RUs
This took 1147ms.
Result count: 65 Used up: 9,05 RUs
This took 968ms.
Result count: 65 Used up: 9,05 RUs
This took 1113ms.
Result count: 65 Used up: 9,05 RUs
This took 971ms.
Result count: 65 Used up: 9,05 RUs
This took 924ms.
Result count: 65 Used up: 9,05 RUs
This took 939ms.
Result count: 65 Used up: 9,05 RUs
This took 1144ms.
Result count: 65 Used up: 9,05 RUs
This took 943ms.
Result count: 65 Used up: 9,05 RUs
This took 957ms.
Result count: 65 Used up: 9,05 RUs
This took 966ms.

Expected behavior For it to be close/match/exceed to the performance of the old v2 library on .net 4.7.2 or drop support for .net. Because, if the performance is like that it is not feasable to use it. You should make people use the v2 library then. Because otherwise they will have a bad experience. It is quite literally 10 times slower than the old v2 library, but uses the exact same RUs.

Environment summary SDK Version: 3.1.1 OS Version: Windows 10 18362

Additional Information

I also tried to do a SQL query, because I initially thought the LINQ implementation might not be very good (yet). The results are similar.

I also tried it with the StreamingAPI now it takes just about the same amount of time. Even if I skip JSON parsing all together and just read the strings into a List. It still takes roughly 1 second to execute, it might be bit faster (didn´t compare exactly). Point is that is not why it´s that much slower.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:29 (14 by maintainers)

github_iconTop GitHub Comments

2reactions
bartelinkcommented, Aug 30, 2019

suggest reposting those with a release build run with ctrl-F5. There’s no point in doing any perf numbers on a debug build and/or with a debugger attached

1reaction
bartelinkcommented, Sep 4, 2019

It’s worth mentioning the quality/nature of the network when doing so - i.e. running in a VM in the data centre would be optimal (and would pretty much exclude the network as a factor) but not necessary (and of course the connection mode, which becomes more significant when you’re measuring the real thing)

Read more comments on GitHub >

github_iconTop Results From Across the Web

.NET Framework 4.7 performance : r/csharp
NET Core SDK is still a beta. The runtime may be much nicer (and faster!) than the old one, but the tooling used...
Read more >
.NET framework 4.7.2 to .NET core 3.1 Identical code, 10x ...
NET application and we plan to move to .NET core. While testing stuff I found the code slow. I did a minimal benchmark...
Read more >
Performance Improvements in .NET 7
NET 7 is fast. Really fast. This post deep-dives into hundreds of performance improvements that contributed to that reality.
Read more >
Serialization Performance Update With .NET 4.7.2 - Alois Kraus
NET Core is slower but some serializers targeting .NET Standard execute workarounds for early .NET Core versions. As always you should measure ...
Read more >
dotnet core slow on Raspberry Pi 2
I was hoping to write my application in C# but unless I can get the performance issue resolved it looks like PHP is...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found