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

Fix memory leak as transactions are continuously received. #644

Merged
merged 25 commits into from
Apr 7, 2019

Conversation

yongjiema
Copy link
Contributor

@yongjiema yongjiema commented Mar 19, 2019

Closes #509

Copy link
Member

@vncoelho vncoelho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds very good, this has been in the list of pending things. Thanks for this good innitiave.

I will review and test asap.

neo/Network/P2P/ProtocolHandler.cs Outdated Show resolved Hide resolved
neo/Network/P2P/ProtocolHandler.cs Outdated Show resolved Hide resolved
neo/Network/P2P/ProtocolHandler.cs Outdated Show resolved Hide resolved
neo/Network/P2P/ProtocolHandler.cs Outdated Show resolved Hide resolved
neo/Network/P2P/ProtocolHandler.cs Outdated Show resolved Hide resolved
@jsolman jsolman changed the title Fix memory leak if connections keep connecting Fix memory leak as transactions are continuously received. Mar 19, 2019
@shargon
Copy link
Member

shargon commented Mar 20, 2019

Dictionary is not needed, i changed it to a simple List, we save more memory

@jsolman
Copy link
Contributor

jsolman commented Mar 20, 2019

@shargon I disagree with your change to a list. Lookup is slow which is what is normally being done, and we want to hold 32K or more items.

@shargon
Copy link
Member

shargon commented Mar 20, 2019

Contains method is the same in a List and a Dictionary, we can use a SortedList or made some benchmarks, but one Dictionary require a Key and a Value, null as value is weird, is a waste of memory and resources ...

@shargon
Copy link
Member

shargon commented Mar 20, 2019

SortedList is based on a Dictionary too :(, we should create our SortedList if we want to improve the performance.

Maybe we should use a SortedSet

@vncoelho
Copy link
Member

This PR closes #509.

Let's do the Bloom Filter...kkkkkkkkkkkkk
I am joking. Let's go in the current direction.

@shargon
Copy link
Member

shargon commented Mar 20, 2019

@jsolman what do you think like this?

using System;
using System.Linq;
using System.Collections.Generic;

namespace Neo.IO.Caching
{
    internal class FIFOSet<T> where T : IEquatable<T>
    {
        private int maxCapacity;
        private int removeCount;
        private SortedSet<T> orderedList;

        public FIFOSet(int maxCapacity, decimal? batchSize = 0.1m)
        {
            if (maxCapacity <= 0) throw new ArgumentOutOfRangeException(nameof(maxCapacity));
            if (batchSize <= 0) throw new ArgumentOutOfRangeException(nameof(batchSize));

            this.maxCapacity = maxCapacity;
            this.removeCount = batchSize != null ? (int)(batchSize <= 1.0m ? maxCapacity * batchSize : maxCapacity) : 1;
            this.orderedList = new SortedSet<T>();
        }

        public bool Add(T item)
        {
            if (orderedList.Contains(item)) return false;
            if (orderedList.Count >= maxCapacity)
            {
                if (removeCount == maxCapacity)
                {
                    orderedList.Clear();
                }
                else
                {
                    for (int i = 0; i < removeCount; i++)
                        orderedList.Remove(orderedList.First());
                }
            }
            orderedList.Add(item);
            return true;
        }
    }
}

@jsolman
Copy link
Contributor

jsolman commented Mar 21, 2019

@shargon a hashset that has linked list nodes in the values is fastest and best. (SortedSet is not optimal for this use - it uses a red black tree)

@yongjiema
Copy link
Contributor Author

Contains method is the same in a List and a Dictionary, we can use a SortedList or made some benchmarks, but one Dictionary require a Key and a Value, null as value is weird, is a waste of memory and resources ...

From the document and the source code, I can see it lookup from hashtable, that's why I chosen it.

@jsolman
Copy link
Contributor

jsolman commented Mar 21, 2019

@yongjiema I think you made a reasonable choice

@jsolman
Copy link
Contributor

jsolman commented Mar 21, 2019

IMO memory savings is trivial and we can just revert this: 27bf008

@shargon
Copy link
Member

shargon commented Mar 21, 2019

I will make some benchmarks

@shargon
Copy link
Member

shargon commented Mar 21, 2019

According to my tests, we should use a HashSet

BenchmarkDotNet=v0.11.3, OS=Windows 10.0.17134.648 (1803/April2018Update/Redstone4)
Intel Core i7-6700HQ CPU 2.60GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
Frequency=2531253 Hz, Resolution=395.0613 ns, Timer=TSC
.NET Core SDK=2.2.104
  [Host]     : .NET Core 2.2.2 (CoreCLR 4.6.27317.07, CoreFX 4.6.27318.02), 64bit RyuJIT
  DefaultJob : .NET Core 2.2.2 (CoreCLR 4.6.27317.07, CoreFX 4.6.27318.02), 64bit RyuJIT


            Method |         Mean |        Error |       StdDev |
------------------ |-------------:|-------------:|-------------:|
         SortedSet |     855.6 ms |    10.556 ms |     9.874 ms |
              List | 165,354.0 ms | 3,204.526 ms | 2,997.516 ms |
 OrderedDictionary |     295.0 ms |     3.159 ms |     2.638 ms |
           HashSet |     119.6 ms |     2.350 ms |     3.517 ms |

The test is like this:

 const int maxCapacity = 32_000;
        const int add = 64_000;

        UInt160[] samples = new UInt160[100_000];

        [GlobalSetup]
        public void Setup()
        {
            var random = new Random();

            for(var x = 0; x < samples.Length; x++)
            {
                var item = new byte[20];
                random.NextBytes(item);

                samples[x] = new UInt160(item);
            }
        }

        [Benchmark]
        public void SortedSet()
        {
            var list = new FIFOSortedSet<UInt160>(maxCapacity);
            for(var x = 0; x < add; x++)
            {
                list.Add(samples[x]);
            }
        }

neo/Settings.cs Outdated Show resolved Hide resolved
@jsolman
Copy link
Contributor

jsolman commented Mar 21, 2019

@shargon you cannot use only a hash set. You will not have fifo behavior with hashset on its own. You would have to benchmark hashset with list nodes in the values and handling the links (which is basically what OrderedDictionary is doing). The fact that ordered dictionaries holds references to values is likely not going to slow it down much so writing our own data structure will just save a small amount of memory.

@jsolman
Copy link
Contributor

jsolman commented Mar 21, 2019

One thing to note though if we do write our own data structure that uses a hashset in combination with linked list nodes, we could use a singly linked list, since we only need the FIFO behavior, which will be even a little more space efficient than the OrderedDictionary that uses a doubly linked list internally.

@shargon
Copy link
Member

shargon commented Mar 21, 2019

I understand you ... then i will revert it to use OrderedDictionary but in the future we should use other methods because this one use more memory.

shargon
shargon previously approved these changes Mar 22, 2019
neo/Settings.cs Outdated Show resolved Hide resolved
@erikzhang
Copy link
Member

Anyone working on this?

@shargon
Copy link
Member

shargon commented Mar 26, 2019

If we decide to use 2x the mempool size, i can do it

@jsolman
Copy link
Contributor

jsolman commented Mar 26, 2019

@shargon go for it

@shargon shargon self-assigned this Mar 26, 2019
- Rename Settings as the class name
- Remove Settings and hardoce a good value
@shargon
Copy link
Member

shargon commented Mar 26, 2019

@yongjiema , please take a look at yongjiema#1

@erikzhang
Copy link
Member

Any update?

@erikzhang erikzhang dismissed jsolman’s stale review April 6, 2019 03:56

The problem has been fixed.

@shargon shargon merged commit ef4cfcd into neo-project:master Apr 7, 2019
Thacryba pushed a commit to simplitech/neo that referenced this pull request Feb 17, 2020
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.

6 participants