有效地存储数据以节省内存使用量,而无需在C#中进行查找

时间:2019-02-08 08:53:16

标签: c#

根据我的特工所处的位置及其旋转,我需要确定距墙的距离。由于此函数需要一点时间并且需要多次调用,因此我的想法是通过离散化位置x和y以及角度来存储距离。

因此,我的函数的调用方式如下:

float GetWallDistance(int x, int y, int angle)
{
    return CalcWallDistance(x, y, angle);
}

其中x和y的范围是0到500,角度范围是0到360。我的第一个想法是将其存储在多维数组中,如下所示:

使用float[,,] saveArray = new float[500, 500, 360];

在某个地方初始化数组
float GetWallDistance(int x, int y, int angle)
{

    float distance = 0;

    if(saveArray[x, y, angle] == 0)
    {
        distance = CalcWallDistance(x, y, angle);
        saveArray[x, y, angle] = distance;
    }
    else
    {
        distance = saveArray[x, y, angle];
    }

    return distance;

}

这极大地缩短了计算时间,但是这里的问题是saveArray占用了很大的内存,并且代理很可能不会遍历整个500 x 500 x 360搜索空间。因此,大量内存将一无所获。

因此,我使用字典来更有效地存储数据,如下所示:

使用Dictionary<double, float> saveDictionairy = new Dictionary<double, float>();

在某处初始化字典
float GetWallDistance(int x, int y, int angle)
{
    double key = (double)x * 1000 + (double)y + (double)angle/1000
    float distance = 0;

    if(!saveDictionairy.TryGetValue(key, out distance))
    {
        distance = CalcWallDistance(x, y, angle);
        saveDictionairy.Add(key, distance);
    }

    return distance;
}

(我尝试使用字符串作为字典的键,但似乎将x,y和angle串联显然要花费相当长的时间)

此方法的确确实提高了内存效率,但相对于索引多维数组,使用键项的字典的查找时间增加了很多。

有人知道如何以一种易于查找的方式有效地存储数据吗?

2 个答案:

答案 0 :(得分:3)

.NET Dictionary使用快速算法,但开销仍然很高。我曾尝试过使其速度更快。我发现通过删除不需要的内容并进行其他设计更改,可以使速度提高6倍。

例如,Dictionary使用模运算符将哈希码映射到存储桶。 %出奇地慢。我认为这需要31个CPU周期。当我将其替换为hashCode & bucketCountMask时,其中存储桶数为2的幂,而bucketCountMaskbuckets.Length - 1时,我立即实现了巨大的性能提升。

我还删除了对删除项目和迭代器version功能的支持。我直接公开了slots数组,以便调用者可以直接更改其中的数据。

此自定义类型的速度更快,因为它更符合我的需求,并且API的使用难度更大。

GitHub上的

.NET代码包含DictionarySlim类型供内部使用。也许您可以使用它。

答案 1 :(得分:2)

从当前列出的选项来看,就性能和内存分配而言,矩阵方法似乎是最好的选择。

我已经运行了以下基准测试:

        [Benchmark(Baseline = true)]
        public void MatrixTest()
        {
            // float[,,] saveArray = new float[501, 501, 361];

            for (int x = 0; x <= 500; x++)
                for (int y = 0; y <= 500; y++)
                    for (int angle = 0; angle <= 360; angle++)
                        if (saveArray[x, y, angle] == 0) saveArray[x, y, angle] = 42;
        }

        [Benchmark]
        void IntKeyDictionaryTest()
        {
            // Dictionary<int, float> saveDictionary = new Dictionary<int, float>();

            for (int x = 0; x <= 500; x++)
                for (int y = 0; y <= 500; y++)
                    for (int angle = 0; angle <= 360; angle++)
                    {
                        int key = (x << 18) | (y << 9) | (angle);
                        if (!saveDictionary.TryGetValue(key, out float d)) saveDictionary[key] = 42;
                    }
        }
        [Benchmark]
        void DoubleKeyDictionaryTest()
        {
            // Dictionary<double, float> saveDictionary = new Dictionary<double, float>();

            for (int x = 0; x <= 500; x++)
                for (int y = 0; y <= 500; y++)
                    for (int angle = 0; angle <= 360; angle++)
                    {
                        double key = (double)x * 1000 + (double)y + (double)angle / 1000l;
                        if (!saveDictionary.TryGetValue(key, out float d)) saveDictionary[key] = 42;
                    }
        }

具有以下结果:

                  Method |        Mean |     Error |    StdDev | Ratio | RatioSD | Gen 0/1k Op | Gen 1/1k Op | Gen 2/1k Op | Allocated Memory/Op |
------------------------ |------------:|----------:|----------:|------:|--------:|------------:|------------:|------------:|--------------------:|
              MatrixTest |    727.9 ms |  5.733 ms |  5.363 ms |  1.00 |    0.00 |           - |           - |           - |                   - |
    IntKeyDictionaryTest |  4,682.1 ms | 12.017 ms | 11.241 ms |  6.43 |    0.05 |           - |           - |           - |                   - |
 DoubleKeyDictionaryTest | 12,804.1 ms | 66.425 ms | 62.134 ms | 17.59 |    0.17 |           - |           - |           - |                   - |

因此,我知道x,y和angle可以分别用9位表示的事实,我设法为您的字典提供了一个更有效的键=>总共27bits可以容纳一个int。 无论如何,矩阵方法似乎是赢家。