C# 使用大量实例优化.NET应用程序的内存占用

C# 使用大量实例优化.NET应用程序的内存占用,c#,.net,performance,optimization,memory,C#,.net,Performance,Optimization,Memory,由于性能原因,我有一个应用程序在内存中保存了大量实例,我不想将其写入磁盘或任何其他地方,只需将其全部保存在内存中即可 public class MyObject { public string Name; public object Tag; public DateTime DateTime1; public DateTime DateTime2; public DateTime DateTime3; public long Num1; p

由于性能原因,我有一个应用程序在内存中保存了大量实例,我不想将其写入磁盘或任何其他地方,只需将其全部保存在内存中即可

public class MyObject
{
    public string Name;
    public object Tag;
    public DateTime DateTime1;
    public DateTime DateTime2;
    public DateTime DateTime3;
    public long Num1;
    public uint Num2;
    public uint Num3;
    public ushort Num4;
}
在许多情况下,我实际上没有使用所有字段,或者没有利用字段的整体大小。因此,我认为可以将整个类转移到一个带有属性的接口中,并使许多实现类以不同的方式存储数据:使用较小的字段(例如int而不是long)并省略一些未使用的字段

例如:

public interface IMyObject
{
    string Name { get; set; }
    object Tag { get; set; }
    DateTime DateTime1 { get; set; }
    DateTime DateTime2 { get; set; }
    DateTime DateTime3 { get; set; }
    long Num1 { get; set; }
    uint Num2 { get; set; }
    uint Num3 { get; set; }
    ushort Num4 { get; set; }
}

public class MyObject1 : IMyObject
{
    public string Name { get; set; }
    public object Tag { get; set; }
    public DateTime DateTime1 { get; set; }
    public DateTime DateTime2 { get; set; }
    public DateTime DateTime3 { get; set; }
    public long Num1 { get; set; }
    public uint Num2 { get; set; }
    public uint Num3 { get; set; }
    public ushort Num4 { get; set; }
}

public class MyObject2 : IMyObject
{
    private int _num1;

    public string Name { get; set; }
    public object Tag { get; set; }
    public DateTime DateTime1 { get; set; }
    public DateTime DateTime2 { get; set; }
    public DateTime DateTime3 { get; set; }
    public long Num1
    {
        get { return _num1; }
        set { _num1 = (int)value; }
    }
    public uint Num2 { get; set; }
    public uint Num3 { get; set; }
    public ushort Num4 { get; set; }
}

public class MyObject3 : IMyObject
{
    public string Name { get; set; }
    public object Tag { get; set; }
    public DateTime DateTime1
    {
        get { return DateTime.MinValue; }
        set { throw new NotSupportedException(); }
    } 
    public DateTime DateTime2 { get; set; }
    public DateTime DateTime3 { get; set; }
    public long Num1 { get; set; }
    public uint Num2 { get; set; }
    public uint Num3 { get; set; }
    public ushort Num4 { get; set; }
}

// ...
理论上,通过这种方法,我实际上可以减少内存占用,但实际上,正如您所看到的,这种方法的问题是,它将导致所有情况下的笛卡尔积,其中包含更小和省略的字段,丑陋和大的代码,这些代码在将来编写后无法维护

关于弦的另一个想法:

.NET应用程序中以UTF-16编码表示的所有字符串。如果我能将其编码为UTF-8,它将减少字符串所用内存的x2倍。

使用它怎么样? 可以动态指定要使用的字段

编辑:
我肯定会调查
String
实习

此外,还有

如何使用? 可以动态指定要使用的字段

编辑:
我肯定会调查
String
实习


另外,通过查看您的个人资料,我将做一个赌注,猜测“Name”属性实际上是一个文件路径。如果空间比时间更重要,那么您可以使用编码方案来表示路径,其中可能存在大量重复数据


将文件路径表示为一个路径,该路径是一个int数组,文件名是一个字符串和实际文件名(这可能更为唯一,因此不值得编码)。您可以将路径拆分为其组成部分,然后使用两个字典来存储正向和反向查找。通过这种方式,您可以将路径缩减为整数数组。比字符串小得多。

通过查看您的个人资料,我将进行一次下注,并猜测“Name”属性实际上是一个文件路径。如果空间比时间更重要,那么您可以使用编码方案来表示路径,其中可能存在大量重复数据


将文件路径表示为一个路径,该路径是一个int数组,文件名是一个字符串和实际文件名(这可能更为唯一,因此不值得编码)。您可以将路径拆分为其组成部分,然后使用两个字典来存储正向和反向查找。通过这种方式,您可以将路径缩减为整数数组。比字符串小得多。

以UTF8格式存储字符串:

byte[] asciiStr = System.Text.Encoding.UTF8.GetBytes("asdf");

string text = System.Text.Encoding.UTF8.GetString(asciiStr);
(编辑:起初以为你需要ASCII)

想法1: 如果您希望大多数值在大多数情况下不会被填充,那么可以将每个字段存储在某种类型的单独键值查找数据结构中——字典、带二进制搜索的有序列表、二叉树等。。带有二进制搜索的有序列表可能是最节省空间的,尽管查找是O(logn)

因此,您可以使用

Dictionary<int, string> names; // or List<Tuple<int,string>> names;
Dictionary<int, object> tags;
Dictionary<int, DateTime> datetime1s;
...

如果标记引用一个活动对象,您可能需要在序列化之外将其单独抛出到包装器结构中。或者,就像第一个想法一样,您可以只存储一个int,标识标记,然后在外部有一个列表>,它保存对标记的实际引用。

以UTF8存储字符串:

byte[] asciiStr = System.Text.Encoding.UTF8.GetBytes("asdf");

string text = System.Text.Encoding.UTF8.GetString(asciiStr);
(编辑:起初以为你需要ASCII)

想法1: 如果您希望大多数值在大多数情况下不会被填充,那么可以将每个字段存储在某种类型的单独键值查找数据结构中——字典、带二进制搜索的有序列表、二叉树等。。带有二进制搜索的有序列表可能是最节省空间的,尽管查找是O(logn)

因此,您可以使用

Dictionary<int, string> names; // or List<Tuple<int,string>> names;
Dictionary<int, object> tags;
Dictionary<int, DateTime> datetime1s;
...
如果标记引用一个活动对象,您可能需要在序列化之外将其单独抛出到包装器结构中。或者,就像在第一个想法中一样,您可以只存储一个int,标识标记,然后在其外部有一个列表>,其中包含对标记的实际引用。

想法:

  • 有共享的字符串吗?加载数据时,您可以使用自定义的interner,以确保所有这些都不重复(注意:不要使用内置的interner,因为这样会使其饱和;即使是
    口述的
    也可以)
  • 是否有任何其他公共元素可以合理地被复制,并可能被移动到对象中?您仍然有参考字段的成本,但希望这(以及新对象)是一个净收益
  • 如果有大量类似的实体,它们中的任何一个都可以建模为不可变的值类型吗?这通常不是我的首选选项,但这样做的一个优点是,您可以将它们固定在一个阵列中:
    • 您可以获得每个实体的对象引用和对象标题的价格
    • 您可以使用数组中的偏移量(
      int
      )而不是引用;对于64位,这是一个不错的节省加起来
  • 您似乎建议使用稀疏对象方法;事实上,你想要避免笛卡尔积,但同时对于人数较少的成员,你描述的财产袋在内存中可能会更昂贵;另外,由于您提到这样做是为了提高性能,我怀疑这也会损害CPU
  • DateTime
    s-例如,它们是否总是整天?如果只使用
    int
    进入一个纪元的天数,您会惊讶地发现您可以获得什么
    • 想法:

      • 有共享的字符串吗?加载时,您可以使用自定义interner