3

我有一段代码可以将大型字符串矩阵转换为MyClass. MyClass是我编写的一个小类,它存储有关每个字符串的一些信息,如下所示:

class MyClass
{
    public MyEnum Class { get; private set; }

    public int A { get; private set; }
    public int B { get; private set; }
    public int C { get; private set; }
    public int D { get; private set; }
}

目前该软件能够处理 5-20 列乘以 100 万行的矩阵,但我想将行数增加到接近 1000 万行。我不相信我可以做很多事情来减少字符串矩阵的占用,但我想减少MyClass.

我可以使用short作为列的类型Abyte作为B,C和的类型D,尽管这需要对代码进行相当大的重构。

我的问题,或者更确切地说是问题:

  1. 是否值得重构代码以使用shortand byte
  2. 我还应该重构MyEnum为 typebyte吗?
  3. 我还能做些什么来提高课堂效率吗?

非常感谢您的宝贵时间!

编辑:更多上下文 - 的矩阵MyClass是从字符串矩阵创建的,用于分析。字符串矩阵是通过普通网络连接从文本文件中提取出来的,因此将任务分成更小的块并不理想。

4

5 回答 5

4

到目前为止,假设您的所有属性都由实例变量实现,并且您在 64 位机器上运行,MyClass 的实例为 4B*4 + 8B = 24B。此外,由于您使用的是类(引用类型),因此 MyClass 的矩阵每个单元格的权重为 8B。这意味着您每个单元格使用 32B。然后,MyClass 的 10Mx20 矩阵使用大约 6.4GB(对于这些大小,您必须使用 64 位二进制),它可能不止于此,因为我忽略了内存对齐要求。

如果从类切换到结构(值类型),矩阵将直接存储 MyClass 实例,而不是指向 MyClass 实例的指针。因此,您将为每个实例节省 8B。现在,内存使用量下降到 4.8GB。

如果您进一步调整实例变量,使用 1 个短字节和 3 个字节,并将枚举转换为字节,每个实例将仅使用 6B。因此,总内存使用量将降至 1.2GB。

无论如何,它不止于此,因为托管环境在每个对象中存储了一些其他元数据,并且因为内存对齐需要填充对象以获得更快的访问时间。

PS:您实际上不需要更改属性的返回类型。您可以封装类型更改,并在 MyClass 的实现中执行强制转换,例如:

struct MyClass
{
  private short a; //Also consider ushort, if you need it
  //...

  public int A
  {
    get { return a; //Automatic promotion }
    private set
    {
      a = (short) value;
      System.Diagnostics.Debug.Assert(a == value, "Integer overflow");
    }
  }

  //...
}

这样,优化将对使用 MyClass 的代码透明。

于 2013-08-06T16:30:40.863 回答
2

您可以做很多事情来减小字符串矩阵的大小,当然这取决于字符串包含的内容。如果你有很多重复的字符串,你可以使用字符串实习建立一个字符串池

如果您的字符串没有重复,但通常是 ASCII 或其他一些单字节编码(或大多数单字节字符的 UTF-8),您可以通过构建字符串资源表来节省大量内存。有关介绍,请参阅减少字符串所需的内存

对于您的MyClass,您需要为每个实例支付 16 字节的分配开销,这几乎与数据本身占用的一样多。struct如果成员都是不可变的,我建议将其设为 a 。它们似乎是公开不变的。你私下做什么,我不知道。但是这样的事情:

[StructLayout(LayoutKind.Sequential, Pack=1)]
struct MyStruct
{
    public readonly MyEnum Class;
    public readonly int A;
    public readonly int B;
    public readonly int C;
    public readonly int D;

    public MyStruct(MyEnum cls, int a, int b, int c, int d)
    {
        Class = cls;
        A = a;
        B = b;
        C = c;
        D = d;
    }
}

每个实例总共有 20 个字节,没有每个实例的分配开销。因此,您的 1000 万行乘 20 列将是 (10M * 20 * 20),即大约 4 GB。在 .NET 4.5 中,您可以使用gcAllowVeryLargeObjects配置设置来创建这么大的数组。

但是请注意,您可能会遇到性能问题。考虑这段代码:

MyStruct m = MyArray[x,y];
// now access fields of m

使用一个结构,它MyArray[x,y]. 这意味着复制 20 个字节。这也意味着如果您修改m.A,该更改将不会反映在数组中。您要么必须将其复制回来(即MyArray[x,y] = m;),要么完全放弃中间变量并编写MyArray[x,y].A = 5;

当然,如果您的结构是不可变的,那么您就不存在复制回问题。

使用 C#可以处理内存的大量项目,但您必须对如何操作具有创造性。我发现以这种方式使用结构非常有效,特别是如果它们是不可变的。

于 2013-08-06T18:01:54.507 回答
1

是 16 位

int是 32 位

您可以设置 Enum 的大小而无需任何实际工作,如此处所述

enum Days : byte {Sat=1, Sun, Mon, Tue, Wed, Thu, Fri};

这意味着您可以将班级人数减少一半。如果这对工作量来说足够好 - 这取决于你

于 2013-08-06T16:02:39.980 回答
0

在使您的类更小方面,您使用其他数据类型的假设是正确的。这将减少分配的整个内存量。就内存中的数据表示而言,您似乎创造了某种花样,是吗?如果是这种情况,还有其他优化,主要称为对集合的引用,意思是,您没有在每列中存储真实值您正在存储对属于具有唯一条目的字典的其他值的引用。此外,您必须以另一种方式调整您的数据。不要认为是面向行的,而是在内存中(或至少在你的大脑中)切换到面向列的数据呈现。

SAP hana 使用这些技术将大量数据保存在内存中,而不是保存在 hd 上。

于 2013-08-06T16:02:05.710 回答
0

如果您使用类,则可以通过创建 的 n 个子类来获得增益MyClass,每个子类一个MyEnum(如果MyEnum具有离散数量的值),然后删除MyEnum.

这只有在MyClass课程明确的情况下才有效。

于 2013-08-06T17:10:10.490 回答