C# 字典的任何现有实现都是无垃圾的吗?
我们有一个巨大的程序,产生了太多的垃圾。这会影响性能,每秒产生数千个软页面错误,减少延迟,影响吞吐量,等等 罪魁祸首之一是C# 字典的任何现有实现都是无垃圾的吗?,c#,.net,visual-studio,.net-4.0,C#,.net,Visual Studio,.net 4.0,我们有一个巨大的程序,产生了太多的垃圾。这会影响性能,每秒产生数千个软页面错误,减少延迟,影响吞吐量,等等 罪魁祸首之一是System.Collections.Generic.Dictionary,因此我们希望将其替换为一个版本,允许我们预先分配10000个条目,然后动态重用这些条目 是否有任何现有的字典实现允许预先分配条目,并且不会在运行时生成垃圾?我们最终决定不使用无垃圾字典,因为它不会对整个GC造成太大影响 下面是一个无垃圾字典的实现。它的内存不足,但它工作得很好 若要使用,请实例化一个新
System.Collections.Generic.Dictionary
,因此我们希望将其替换为一个版本,允许我们预先分配10000个条目,然后动态重用这些条目
是否有任何现有的
字典
实现允许预先分配条目,并且不会在运行时生成垃圾?我们最终决定不使用无垃圾字典,因为它不会对整个GC造成太大影响
下面是一个无垃圾字典的实现。它的内存不足,但它工作得很好
若要使用,请实例化一个新字典,其中有足够的插槽用于放置潜在的整数键。例如,如果主整型键的范围在0到50000之间,则在实例化时需要传入50000:
MyIntegerKeyDictionary = new MyIntegerKeyDictionary<int, MyClass>(50000);
MyIntegerKeyDictionary=新的MyIntegerKeyDictionary(50000);
如果密钥的范围在200000到250000之间,请使用50000个潜在密钥对其进行实例化,它将自动“重新设置”密钥的基础(基于它看到的第一个密钥),使其范围在200000到250000之间
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;
}
}
*/
}
}
使用系统;
使用System.Collections.Concurrent;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用系统线程;
使用MyLogType;
名称空间MyHelper
{
///
///注意:非线程安全,仅单独运行。
///
///我们要添加到词典中的项的类型。
公共类MyIntegerKeyDictionary,其中T:class
{
///
///存储条目的数组。
///
易失性私有T[]阵列使用;
///
///允许我们检查最大尺寸,以防万一。
///
私有只读int-SizeInternal;
///
///跟踪词典中的当前项数。
///
公共整数计数=0;
///
///基数。对于以大基数开始的数字,这允许索引在不分配兆字节内存的情况下工作。
///
private int BaseNumberToAdd{get;set;}
///
///构造器。
///
///字典的大小。
公共MyIntegerKeyDictionary(整数大小)
{
//创建数组以容纳n个项目。
ArrayToUse=新的T[尺寸];
//我们必须触摸所有这些条目,以强制现在(而不是以后)出现页面错误。
for(int i=0;i=this.SizeInternal)
{
Console.Write(string.Format(“{0}警告W20120907-3756.TryAdd.试图添加索引{1}大于max{2}的键\n”,
MyLog.NPrefix(),Key,this.SizeInternal));
返回false;
}
if(ArrayToUse[Key]==null)
{
联锁增量(参考计数);
}
ArrayToUse[键]=值;
返回true;
}
///
///从字典中删除一项。
///
///我们要删除的密钥。
///如果可以删除该项,则为True;如果不能,则为false。
公共bool TryRemove(int键)
{
if(BaseNumberToAdd==int.MinValue)
{
Console.Write(string.Format(“{0}警告W20120907-8756.TryRemove)。试图删除字典中没有任何项的项{1}。\n”,
MyLog.NPrefix(),Key));
返回false;
}
Key=Key-BaseNumberToAdd;
如果(键<0)
{
Console.Write(string.Format(“{0}警告W20120907-9756.TryRemove)。试图删除索引{1}小于0的键。\n”,
MyLog.NPrefix(),Key));
返回false;
}
如果(键>=this.SizeInternal)
{
Console.Write(string.Format(“{0}警告W20120907-6756.TryRemove)。试图删除索引为“{1}”且大于max{2}”的键\n”,
MyLog.NPrefix(),Key,this.SizeInternal));
返回false;
}
if(数组使用[Key]!=null)
{
联锁。减量(参考计数);
}
ArrayToUse[Key]=null;
返回true;
}
///
///索引器。
///
///钥匙。
///价值观。
此[int-key]的公共密钥
{
得到
{
T值返回;
TryGetValue(键,输出值返回);
返回值返回;
}
}
///
///尝试获取该值。
///
///钥匙。
///价值观。
///如果该值存在,则为True;如果不存在,则为false。
公共bool TryGetValue(int键,out T值)
{
值=空;
if(BaseNumberToAdd==int.MinValue)
{
Console.Write(string.Format(“{0}警告W20120907-8756.TryGetValue)。试图检索字典中尚未包含任何项的项{1}。\n”,
MyLog.NPrefix(),Key));
返回false;
}
Key=Key-BaseNumberToAdd;
if(ArrayToUse[Key]==null)
{
返回false;
}
值=数组使用[键];