为什么是Dictionary.ContainsKey()& ToString()导致GC Alloc?

时间:2018-05-12 06:15:26

标签: c# dictionary garbage-collection override tostring

除了大约1k个实例经常运行自己的ContainsKey()ToString()这一事实之外,我不得不提供更多详细信息。

位置只是我个人替换Unity Vector3以满足我的需求:

[Serializable] public struct Location
{
    public double X;
    public double Y;
    public double Z;

    public Location(double x, double y, double z) : this()
    {
        X = x;
        Y = y;
        Z = z;
    }

    public override string ToString()
    {
        return String.Format("{0}, {1}, {2}", X, Y, Z);
    }
}

(我知道我打破Structs的某种规则..只是不确定如何以另一种方式实现我的需求。)

以下是Profiler运行的屏幕截图:

Unity_Profiler_ToString_ContainsKey_GC_Alloc

正如你所看到的,对于大多数时间线来说,它是稳定的,然后在我的实例达到大约1k(数量)之后突然发生(他们以100-250左右开始)CPU和内存由于似乎看起来很疯狂成为GC分配。我一直在寻找能够更好地清理的东西,但我发现甚至导致任何GC分配都是在我运行的时候:

if (_dictionary.ContainsKey(key)) {...}

以及使用以下命令重命名Unity GameObjects时

part.name = "Part: " + part.Location.ToString();

如果它仅仅涉及查找所需的不可避免的时间,那么Dictionaries是否有任何替代方案往往运行速度更慢但导致GC分配更少,并且有更有效的方式{{} 1}} override方法?

添加: 我的词典是关键:(我的个人结构)位置,值:类实例。

1 个答案:

答案 0 :(得分:1)

将评论转化为答案......

您的ToString()方法总是会创建一个新字符串,所以这并不奇怪。但是,您还使用字符串连接,因此您要创建两个新字符串。您可以通过内联ToString()方法将其减少为1。例如,为简洁起见,使用C#6插值字符串:

var location = part.Location;
part.name = $"Part: {location.X}, {location.Y}, {location.Z}";

对于字典方面,有两个问题:

  • 您没有覆盖EqualsGetHashCode,其中可能表示该值正在装箱,以便调用ValueType中的实现。我不是百分百肯定的;关于拳击的规则可能很复杂。
  • 您没有实施IEquatable<T>,因此非常可能会有任何Equals来电。{/ li>

您可以轻松解决这两个问题:

[Serializable] public struct Location : IEquatable<Location>
{
    public double X;
    public double Y;
    public double Z;

    public Location(double x, double y, double z) : this()
    {
        X = x;
        Y = y;
        Z = z;
    }

    public override string ToString() => $"{X}, {Y}, {Z}";

    public override bool Equals(object obj) =>
        obj is Location loc && Equals(loc);

    public bool Equals(Location other) =>
        X == other.X && Y == other.Y && Z == other.Z;

    public override int GetHashCode()
    {
        // Replace with whatever implementation you want
        int hash = 17;
        hash = hash * 23 + X.GetHashCode();
        hash = hash * 23 + Y.GetHashCode();
        hash = hash * 23 + Z.GetHashCode();
        return hash;
    };
}

(那是使用C#7语法,但是如果你使用现代版本的Unity和VS2017,我希望仍然没问题。如果你使用的是旧版本,你应该能够实现相同的方法略长一点。)