当许多键的值相同时减少数据内存 C#

Reducing data memory when values are identical for many keys C#

我有大量的数据,文本形式如下:

    4c5c,6c4h,486,30
    4c5c,6c5h,486,30
    4c5c,6c6h,458,0
    4c5c,6c7h,648,9
    4c5c,6c8h,648,9
    3sTs,QsKs,182,0
    3sTs,QsAs,182,0
    3sTs,KsAs,743,0
    3sJs,4s5s,495,0
    3sJs,4s6s,625,0
    3sJs,4s7s,739,0
    3sJs,4s8s,739,0
    3sJs,4s9s,739,0
    3sJs,4sTs,739,0
    3sJs,4sQs,182,0
    3sJs,4sKs,739,0
    3sJs,4sAs,625,0
    3sJs,5s6s,625,0
    3sJs,5s7s,739,0
    3sJs,5s8s,739,0

在每一行中,前两项代表一个键,后两项代表值。 例如 调:3sJs,5s8s 值:739,0

我有超过 140 亿行这样的行,并且一直将它们存储在字典中以便快速访问。这是有问题的,因为大约有 250GB 的文本数据存储在字典中时需要大量内存。但是,我注意到多个键的许多值是相同的。是否有一些替代字典的方法可以在减少内存需求的情况下存储这些数据,以利用重复许多值的事实? 我以前没有用 C# 程序使用过数据库,但做过一些 postgres - sqlite 是我的 best/only 明智的选择吗?它会把我的应用程序大小减小到可管理的大小以便分发吗?

在您评论中提到的限制下,您可以将显示的示例数据放在一个 5 维数组中。您要样品,这里就是。我将从您的示例数据中提取第一行:

4c5c,6c4h,486,30

首先,您必须为卡片指定数值。这取决于你如何在 0 到 51 之间对它们进行编号。为简单起见,我将使用

4c = 0
5c = 1
6c = 2
4h = 3

数组应该是这样的:

var data = new short [52, 52, 52, 52, 2];

前四个维度代表键(卡号从 0 到 51),第 5 个维度代表值的索引(0 或 1)。 然后您可以像这样访问您的数组(这里我将设置示例数据中第一行的值):

data[0, 1, 2, 3, 0] = 486;
data[0, 1, 2, 3, 1] = 30;

如果存在无效或不需要的卡牌组合,请不要将它们放入数组中(默认情况下所有元素都初始化为 0)。该数组的大小固定为 29,246,464 字节。通过卡号访问数组元素是最快的方法。但正如我在评论中所写,这仅适用于您提供的数据结构。目前还不清楚其他三张卡是如何关联的,这个数组只能建立你提供的数据样本。还是希望对你有帮助。

我不确定你的值是多少。你能把你的价值观编码成多小?我假设您可以将它们打包成 4 个字节。

所以您的密钥是一个描述 4 张扑克牌的文本字符串。所以你可能的密钥 space 最多是 52^4 ~= 7.3e6。在所有可能的键中,哪一部分会有值?离他们都近吗?

我不会使用文本字符串来表示您的密钥,而是会为每张卡片分配一个数字 (0-51)。然后建一个Int32=k1 + k2*52 + k3*(52^2) + k4*(52^3).

如果要为大多数键存储值,则不需要字典。您可以只使用长度为 52^4 的数组。这样你就不需要为键本身分配任何内存。只需要 4bytes * 52^4 ~= 28MB 的内存。

然后为了将这个数组存储在磁盘上,我会将其保存在一个 zip 存档中。如果你有很多相似的值,哪个应该压缩得很好。

您可能希望将此数据分成多个部分,这样您就不必将整个结构解压到内存中。但这取决于您希望在运行时如何使用这些数据。