-2

我们有一个庞大的程序,它产生了太多的垃圾。这会损害性能,每秒产生数千个软页面错误,减少延迟,损害吞吐量等。

罪魁祸首之一是System.Collections.Generic.Dictionary,因此我们希望将其替换为允许我们预先分配 10,000 个条目的版本,然后即时重用这些条目。

是否有任何现有的实现Dictionary允许预先分配条目,并且不会在运行时产生垃圾?

4

1 回答 1

2

我们最终决定不使用无垃圾字典,因为它对整体 GC 的影响不大。

作为记录,这里是一个无垃圾字典的实现。它的内存很饿,但效果很好。

要使用,请实例化一个新字典,为潜在的整数键提供足够的插槽。例如,如果主整数键的范围在 0 到 50,000 之间,那么在实例化它时需要传入 50,000:

MyIntegerKeyDictionary = new MyIntegerKeyDictionary<int, MyClass>(50000);

如果密钥的范围从 200,000 到 250,000,则使用 50,000 个潜在密钥对其进行实例化,它会自动“重新设置”密钥(基于它看到的第一个密钥),因此它的范围可以从 200,000 到 250,000。

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using MyLogType;

namespace MyHelper
{
/// <summary>
/// NOTE: Non thread safe, only run this individually.
/// </summary>
/// <typeparam name="T">Type of item we are adding to the dictionary.</typeparam>
public class MyIntegerKeyDictionary<T> where T : class
{
    /// <summary>
    /// Array in which we store the entries.
    /// </summary>
    volatile private T[] ArrayToUse;

    /// <summary>
    /// Allows us to check the maximum size, just in case.
    /// </summary>
    private readonly int SizeInternal;

    /// <summary>
    /// Keeps track of the current number of items in the dictionary.
    /// </summary>
    public int Count = 0;

    /// <summary>
    /// Base number. For numbers that start with a huge base, this allows the index to work without allocating megabytes of memory.
    /// </summary>
    private int BaseNumberToAdd { get; set; }

    /// <summary>
    /// Constructor.
    /// </summary>
    /// <param name="Size">Size of the dictionary.</param>
    public MyIntegerKeyDictionary(int Size)
    {
        // Create the array to hold n number of items.
        ArrayToUse = new T[Size];

        // We must touch all of these entries, to force page faults now (rather than later).
        for (int i = 0; i < ArrayToUse.Length; i++)
        {
            ArrayToUse[i] = null;
        }
        BaseNumberToAdd = int.MinValue;

        this.SizeInternal = Size;
    }

    /// <summary>
    /// Add an item.
    /// </summary>
    /// <param name="Key">Key.</param>
    /// <param name="Value">Value.</param>
    /// <returns>True if the item was added, false if not.</returns>
    public bool TryAdd(int Key, T Value)
    {
        if (BaseNumberToAdd == int.MinValue)
        {
            BaseNumberToAdd = Key;
            //Console.Write(string.Format("{0}Message W20120907-3751. TryAdd. Adding first item {1}.\n",
            //MyLog.NPrefix(), Key));
        }
        Key = Key - BaseNumberToAdd;

        if (Key < 0)
        {
            Console.Write(string.Format("{0}Warning W20120907-3750. TryAdd. Attempted to add a key with index {1} which is < 0.\n",
                MyLog.NPrefix(), Key));
            return false;
        }

        if (Key >= this.SizeInternal)
        {
            Console.Write(string.Format("{0}Warning W20120907-3756. TryAdd. Attempted to add a key with index {1} which is > max {2}\n",
                MyLog.NPrefix(), Key, this.SizeInternal));
            return false;
        }

        if (ArrayToUse[Key] == null)
        {
            Interlocked.Increment(ref Count);
        }

        ArrayToUse[Key] = Value;
        return true;
    }

    /// <summary>
    /// Remove an item from the dictionary.
    /// </summary>
    /// <param name="Key">Key that we want to remove.</param>
    /// <returns>True if the item could be removed, false if not.</returns>
    public bool TryRemove(int Key)
    {
        if (BaseNumberToAdd == int.MinValue)
        {
            Console.Write(string.Format("{0}Warning W20120907-8756. TryRemove. Attempted to remove an item without any items in the dictionary yet {1}.\n",
                MyLog.NPrefix(), Key));
            return false;
        }
        Key = Key - BaseNumberToAdd;


        if (Key < 0)
        {
            Console.Write(string.Format("{0}Warning W20120907-9756. TryRemove. Attempted to remove a key with index {1} which is < 0.\n",
                MyLog.NPrefix(), Key));
            return false;
        }

        if (Key >= this.SizeInternal)
        {
            Console.Write(string.Format("{0}Warning W20120907-6756. TryRemove. Attempted to remove a key with index {1} which is > max {2}\n",
                MyLog.NPrefix(), Key, this.SizeInternal));
            return false;
        }

        if (ArrayToUse[Key] != null)
        {
            Interlocked.Decrement(ref Count);
        }

        ArrayToUse[Key] = null;

        return true;
    }


    /// <summary>
    /// Indexer.
    /// </summary>
    /// <param name="key">Key.</param>
    /// <returns>Value.</returns>
    public T this[int key]
    {
        get
        {
            T valueToReturn;
            TryGetValue(key, out valueToReturn);
            return valueToReturn;
        }
    }

    /// <summary>
    /// Attempt to get the value.
    /// </summary>
    /// <param name="Key">Key.</param>
    /// <param name="Value">Value.</param>
    /// <returns>True if the value exists, false if not.</returns>
    public bool TryGetValue(int Key, out T Value)
    {
        Value = null;
        if (BaseNumberToAdd == int.MinValue)
        {
            Console.Write(string.Format("{0}Warning W20120907-8756. TryGetValue. Attempted to retrieve an item without any items in the dictionary yet {1}.\n",
                MyLog.NPrefix(), Key));
            return false;
        }
        Key = Key - BaseNumberToAdd;
        if (ArrayToUse[Key] == null)
        {

            return false;
        }

        Value = ArrayToUse[Key];
        return true;
    }

    /// <summary>
    /// Checks to see if the key exists.
    /// </summary>
    /// <param name="Key">Key index.</param>
    /// <returns>True if the item exists, false if not.</returns>
    public bool ContainsKey(int Key)
    {
        if (Key == 0)
        {
            Console.Write(string.Format("{0}Warning W20120907-1914. ContainsKey. Have not rebased yet. Ignoring query for ContainsKey(0).\n",
                MyLog.NPrefix()));
            return false;
        }

        if (BaseNumberToAdd == int.MinValue)
        {
            Console.Write(string.Format("{0}Warning W20120907-8756. ContainsKey. Attempted to check if Key {1} exists, however BaseNumber is not set yet.\n",
                "", Key));
            return false;
        }
        Key = Key - BaseNumberToAdd;

        if (Key < 0)
        {
            Console.Write(string.Format("{0}Warning W20120907-8756. ContainsKey. Key = {1} which is < 0.\n",
                "", Key));
            return false;
        }

        if (Key >= this.SizeInternal)
        {
            MyLogAsync.LogWarning(string.Format("{0}Warning W20120907-5756. ContainsKey. Key({1}) >= this.SizeInternal ({2}).\n",
                MyLog.NPrefix(), Key, this.SizeInternal));
            return false;
        }

        if (ArrayToUse[Key] == null)
        {
            return false;
        }
        return true;
    }
    /*
    private bool CheckKeyBounds(int Key, string description)
    {
        if (Key < 0)
        {
            MyLogAsync.LogWarning(string.Format("{0}Warning W20120907-8756. {1}. Attempted to add a key with index {2} which is < 0.\n",
                MyLog.NPrefix(), description, Key));
        }

        if (Key >= this.SizeInternal)
        {
            MyLogAsync.LogWarning(string.Format("{0}Warning W20120907-5756. {1}. Attempted to add a key with index {2} which is > max {3}\n",
                MyLog.NPrefix(), description, Key, this.SizeInternal));
            return false;
        }
    }
    */
}
}
于 2012-09-21T11:23:43.513 回答