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

Lockfree round robin #63

Merged
merged 4 commits into from
Dec 7, 2019
Merged

Lockfree round robin #63

merged 4 commits into from
Dec 7, 2019

Conversation

DaniilSokolyuk
Copy link
Contributor

BenchmarkDotNet=v0.12.0, OS=Windows 10.0.17763.475 (1809/October2018Update/Redstone5)
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.0.100
  [Host]     : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT
  DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), X64 RyuJIT

Method Mean Error StdDev Median Gen 0 Gen 1 Gen 2 Allocated
Lock 17.24 ns 0.146 ns 0.136 ns 17.29 ns - - - -
LockFree 16.10 ns 0.021 ns 0.017 ns 16.11 ns - - - -
LockParallel 263,920.42 ns 8,655.977 ns 25,522.345 ns 272,410.38 ns 18.0664 0.2441 - 62901 B
LockFreeParallel 73,642.52 ns 311.213 ns 275.882 ns 73,615.78 ns 8.4229 0.1221 - 28947 B

Benchmark code

using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            BenchmarkRunner.Run<Benchmarks>();
        }
    }

    public class Lock
    {
        private readonly ReadOnlyCollection<object> _httpNodeJSServices;

        private readonly int _maxIndex;
        private readonly object _httpNodeJSServicesLock = new object();
        private int _nextIndex;

        public int Size { get; }

        public Lock()
        {
            var httpNodeJSServices = new ReadOnlyCollection<object>(Enumerable.Range(0, 16).Select(x => new object()).ToList());

            _httpNodeJSServices = httpNodeJSServices;
            Size = httpNodeJSServices.Count;
            _maxIndex = Size - 1;
        }


        internal object GetHttpNodeJSService()
        {
            int index = 0;
            lock (_httpNodeJSServicesLock)
            {
                if (_nextIndex > _maxIndex)
                {
                    _nextIndex = 0;
                }

                index = _nextIndex++;
            }

            return _httpNodeJSServices[index];
        }
    }

    public class LockFree
    {
        private readonly ReadOnlyCollection<object> _httpNodeJSServices;
        private volatile int _nextIndex = -1;

        public int Size { get; }

        public LockFree()
        {
            var httpNodeJSServices = new ReadOnlyCollection<object>(Enumerable.Range(0, 16).Select(x => new object()).ToList());

            _httpNodeJSServices = httpNodeJSServices;
            Size = httpNodeJSServices.Count;
        }

        internal object GetHttpNodeJSService()
        {
            uint index = unchecked((uint)Interlocked.Increment(ref _nextIndex));
            return _httpNodeJSServices[(int)(index % Size)];
        }
    }

    [MemoryDiagnoser]
    public class Benchmarks
    {
        private LockFree _lockFree;
        private Lock _lock;

        [GlobalSetup]
        public void Setup()
        {
            _lock = new Lock();
            _lockFree = new LockFree();
        }

        [Benchmark]
        public void Lock()
        {
            _lock.GetHttpNodeJSService();
        }
        
        [Benchmark]
        public void LockFree()
        {
            _lockFree.GetHttpNodeJSService();
        }

        [Benchmark]
        public void LockParallel()
        {
            Parallel.ForEach(
                Enumerable.Range(0, 2000),
                new ParallelOptions { MaxDegreeOfParallelism = 32 },
                _ => _lock.GetHttpNodeJSService()
            );
        }

        [Benchmark]
        public void LockFreeParallel()
        {
            Parallel.ForEach(
                Enumerable.Range(0, 2000),
                new ParallelOptions { MaxDegreeOfParallelism = 32 },
                _ => _lockFree.GetHttpNodeJSService()
            );
        }
    }
}

@codecov
Copy link

codecov bot commented Dec 6, 2019

Codecov Report

Merging #63 into master will decrease coverage by 0.03%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #63      +/-   ##
==========================================
- Coverage   97.51%   97.48%   -0.04%     
==========================================
  Files          18       18              
  Lines         564      557       -7     
==========================================
- Hits          550      543       -7     
  Misses         14       14
Impacted Files Coverage Δ
...tations/OutOfProcess/Http/HttpNodeJSPoolService.cs 96.42% <100%> (-0.72%) ⬇️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6161538...306e301. Read the comment docs.

@JeremyTCD JeremyTCD added the enhancement New feature or request label Dec 7, 2019
@JeremyTCD
Copy link
Member

Am not familiar with lockless threading but it looks good! Will double check and add some tests before merging.

@JeremyTCD
Copy link
Member

Made some minor changes, mostly just comments for future reference. Will merge if you have no issues with the changes.

@DaniilSokolyuk
Copy link
Contributor Author

Nice! You right volatile does not need, because we use that variable only in interclocked and never again :)

@JeremyTCD JeremyTCD merged commit 8bb7314 into JeringTech:master Dec 7, 2019
@JeremyTCD
Copy link
Member

Thanks for the contribution! It'll be in v5.3.0.

@JeremyTCD
Copy link
Member

Just release v5.3.0 with this addition!

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

Successfully merging this pull request may close these issues.

None yet

2 participants