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

Severe performance degradation with usage of ConcurrentQueue<Scalar> when multi-thread #105

Open
UlyssesWu opened this issue May 9, 2024 · 4 comments

Comments

@UlyssesWu
Copy link
Contributor

UlyssesWu commented May 9, 2024

class ScalarPool
{
public static readonly ScalarPool Shared = new();
readonly ConcurrentQueue<Scalar> queue = new();
public Scalar Rent()
{
if (queue.TryDequeue(out var value))
{
return value;
}
return new Scalar(256);
}

I have a tool to deserialize all assets from my unity project. It's basically calling YamlSerializer.DeserializeMultipleDocumentsAsync combined with Parallel.ForEach.
When I upgraded to the latest VYaml (v0.26) I have observed very obvious performance lost.
The process time increased from 1min to 4min.

It seems that the recently imported ConcurrentQueue<Scalar>.TryDequeue(...) for ScalarPool.Rent() caused the problem.
image

It's spending too much time on SpinWait. I guess the usage of ConcurrentQueue is thread safe but not multi-thread friendly. 🤔
image

And, this is the time cost for the same input, before the ConcurrentQueue was introduced:
image

@hadashiA
Copy link
Owner

Thank you for your report.
I will consider this when I get around to it..

However, if you can, I would be grateful if you could provide me with Benchmark code or similar that can reproduce your problem. Thanks.

@UlyssesWu
Copy link
Contributor Author

Code: (for demo I just read the same file for many times; for actual usage I'll read each asset in project)

using System.Diagnostics;
using VYaml.Serialization;

namespace VYamlBench
{
    internal class Program
    {
        static async Task Main(string[] args)
        {
            var sw = Stopwatch.StartNew();
            await Test();
            sw.Stop();
            Console.WriteLine($"Elapsed: {sw.ElapsedMilliseconds}ms");
        }

        static async Task Test()
        {
            var path = "Level.prefab";
            await Parallel.ForAsync(0, 1000, async (_, token) =>
            {
                await using var fs = new FileStream(path,
                    new FileStreamOptions()
                        { Access = FileAccess.Read, Options = FileOptions.Asynchronous | FileOptions.SequentialScan });

                try
                {
                    await YamlSerializer.DeserializeMultipleDocumentsAsync<dynamic>(fs, YamlSerializerOptions.Standard);
                }
                catch (Exception)
                {
                    Console.WriteLine($"Parsing error");
                }
            });
        }
    }
}

YAML Sample: (borrowed from an open-source unity project) https://github.com/imengyu/Ballance/blob/main/Assets/Game/Levels/%E9%AD%94%E8%84%93%E7%A9%BA%E9%97%B4%E7%AB%99/Level.prefab

With v0.26.0: Elapsed: 42569ms
With v0.14.1: Elapsed: 33275ms

@hadashiA
Copy link
Owner

hadashiA commented Jun 1, 2024

#108 may have improved things a little.

Here are the results of a run in my environment with the loop count set from 1000 to 100.

  • Before: 4309ms
  • After: 3614ms

@UlyssesWu
Copy link
Contributor Author

It's improved, but still worse than 0.14.1.

Test 0.14.1 0.26.0 with PR
VYamlBench (1000 loops) (ms) 30803 43672 39051
My Actual Usage (min) 2:07 4:00+ 3:17

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

No branches or pull requests

2 participants