为什么 Dictionary.ContainsKey() & ToString() 导致 GC Alloc?
Why is Dictionary.ContainsKey() & ToString() causing GC Alloc?
除了有大约 1k 个实例是 运行 它们自己的 ContainsKey()
和 ToString()
的事实之外,我无需提供更多细节。
Location 只是我对 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
时违反了某种规则。只是不确定如何以另一种方式满足我的需求。)
这是分析器的屏幕截图运行:
如您所见,在时间线的大部分时间里,它是稳定的,然后在我的实例达到大约 1k(数量)后突然间,(它们从 100-250 开始)CPU 和内存由于似乎是 GC 分配而变得疯狂。我一直在寻找我可以更好地清理的东西,但我所看到的甚至导致任何 GC 分配是当我 运行 an:
if (_dictionary.ContainsKey(key)) {...}
并且在重命名 Unity 游戏对象时使用:
part.name = "Part: " + part.Location.ToString();
如果它只是与查找所花费的不可避免的时间有关,那么是否有任何替代 Dictionaries
的方法往往运行速度更慢但导致的 GC 分配更少,并且是否有更有效的方法来override
ToString()
方法?
添加: 我的字典是关键:(我的个人结构)位置,值:Class 实例。
正在将评论变成答案...
您的 ToString()
方法总是会创建一个新字符串,所以这不足为奇。但是,您还使用了字符串连接,因此您创建了两个 个新字符串。您可以通过内联 ToString()
方法将其减少为一个。例如,为简洁起见,使用 C# 6 内插字符串:
var location = part.Location;
part.name = $"Part: {location.X}, {location.Y}, {location.Z}";
字典方面,有两个问题:
- 您没有覆盖
Equals
和 GetHashCode
,这 可能 意味着值被装箱以便调用 ValueType
。我对此不是 100% 确定;拳击规则可能很复杂。
- 您没有实施
IEquatable<T>
,因此 非常 任何 Equals
调用都可能是拳击。
您可以轻松解决这两个问题:
[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,那仍然没问题。如果您使用的是旧版本,您应该能够实现相同的方法只是稍微冗长一点。)
除了有大约 1k 个实例是 运行 它们自己的 ContainsKey()
和 ToString()
的事实之外,我无需提供更多细节。
Location 只是我对 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
时违反了某种规则。只是不确定如何以另一种方式满足我的需求。)
这是分析器的屏幕截图运行:
如您所见,在时间线的大部分时间里,它是稳定的,然后在我的实例达到大约 1k(数量)后突然间,(它们从 100-250 开始)CPU 和内存由于似乎是 GC 分配而变得疯狂。我一直在寻找我可以更好地清理的东西,但我所看到的甚至导致任何 GC 分配是当我 运行 an:
if (_dictionary.ContainsKey(key)) {...}
并且在重命名 Unity 游戏对象时使用:
part.name = "Part: " + part.Location.ToString();
如果它只是与查找所花费的不可避免的时间有关,那么是否有任何替代 Dictionaries
的方法往往运行速度更慢但导致的 GC 分配更少,并且是否有更有效的方法来override
ToString()
方法?
添加: 我的字典是关键:(我的个人结构)位置,值:Class 实例。
正在将评论变成答案...
您的 ToString()
方法总是会创建一个新字符串,所以这不足为奇。但是,您还使用了字符串连接,因此您创建了两个 个新字符串。您可以通过内联 ToString()
方法将其减少为一个。例如,为简洁起见,使用 C# 6 内插字符串:
var location = part.Location;
part.name = $"Part: {location.X}, {location.Y}, {location.Z}";
字典方面,有两个问题:
- 您没有覆盖
Equals
和GetHashCode
,这 可能 意味着值被装箱以便调用ValueType
。我对此不是 100% 确定;拳击规则可能很复杂。 - 您没有实施
IEquatable<T>
,因此 非常 任何Equals
调用都可能是拳击。
您可以轻松解决这两个问题:
[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,那仍然没问题。如果您使用的是旧版本,您应该能够实现相同的方法只是稍微冗长一点。)