我一直在尝试优化我为项目编写的内存数据存储,并尝试重写存储核心并使用固定字节结构,但是性能大大下降,所以我想问一下我应该使用哪种方法依照? 这个问题与此Optimizing C# large dataset iterations - External code in profiler and weird behavior有关,在这里我有多个字典,每个字典正好包含100万个100字节长的数组,它们具有某种结构来保存列数据。
有一个“碎片”对象,它具有以下内部存储空间:
private Dictionary<Guid, int> innerCache;
private DataBytes[] innerValues;
private int[] vacancies;
以某种方式进行设置,其中Guid-Int Dictionary用于快速Guid查找,并且INT Value指向innerValues数组的索引。
空缺是已从字典中删除并在数组中“为空”的索引,并且在添加新记录的情况下,将使用新数据重新填充而不是将数据附加到数组的末尾。
它拥有的结构非常简单:
public unsafe struct DataBytes
{
public fixed byte bytes[eMemshard.buffer_size];
}
现在,此数组的第一个字节实际上是在标识记录的类型,将其视为标准数据库中的表标识符。它实际上也是表和列定义字典的索引,然后将其用于将检索到的数据转换为代理对象。
现在我要遍历innerValues结构数组,我只需要查看第一个字节是否大于零即可。
public unsafe void ForEach(Action<DataBytes, byte> act, byte tableIndex)
{
cacheLock.EnterReadLock();
try
{
int l = innerValues.Length;
DataBytes iv;
for (var i = l - 1; i >= 0; i -= 2)
{
iv = innerValues[i];
if (iv.bytes[0] > 0)
{
}
if (i - 1 < 0)
continue;
iv = innerValues[i - 1];
if (iv.bytes[0] > 0)
{
}
}
}
finally
{
cacheLock.ExitReadLock();
}
}
这段代码花费大约200ms的时间来执行所有1000万条记录,但是当我删除条件时,时间降到了0.4ms。
当我使用标准托管阵列时,时间实际上只有大约24ms与0.x ms。使用固定数组实际上不是一个好主意吗?另外,是否有可以加快检索第一个字节的操作?我想消除并跳过不同“表”中的记录。
注意:我知道有现成的解决方案可以将数据存储在内存中,这个问题仅用于我自己的研究,有点个人挑战:)但我相信我为时已晚。
注2:该代码实际上位于.NETCore中,但我相信它并没有太大的区别。
编辑1: 为了帮助他人,以下是一些背后的原因,如果我乍看之下所做的事情乍一看是没有道理的:
为什么要使用字典和数组?为什么不只使用Dictionary?-在数组上进行迭代比枚举Dictionary并在其元素上进行迭代要快得多。
为什么要在for循环中将其计数为0?-与零相比,与零相比,具有性能优势。
为什么每个迭代有多个指令并递减2?-显然这与内存争用有关,我发现它比单指令迭代快约10%。也许有人可以对此有所了解。在迭代中也使用两个以上的指令也可以提高性能,但是可以忽略不计。似乎每次迭代有更多的指令,性能提升是对数的。在获得性能提升和必须处理该步骤中不适合的其余条目之间,似乎有两个甜蜜点。
为什么要在循环外声明vars?-GC喜欢它:)可以通过这种方式消除GC集合。