我有{/ 1}}中名为id2key_value的哈希(ID)映射的键/值对。您可以将此视为一种用行表示类似数据库的表的方法。
我添加了一些辅助函数,通过执行强制转换来简化一些基本数据类型的使用,例如
Dictionary<string, Dictionary<string, string>>
好吧,当我想出一个“cast-cache”的想法时,我觉得我很聪明,它基本上保存了已经解析过的对象,所以我可以跳过对int,bool,DateTime等字符串的解析。,并简单地将它们从缓存中转换为适当的数据类型。像,
public int GetInt(string id, string key)
{
int value = 0;
bool success = int.TryParse(map[id][key], out value);
if (success)
return value;
else
throw new InvalidOperationException(
"Trying to obtain a non-integer value with GetInt().");
}
“cast-cache”只是public int GetInt(string id, string key)
{
if (cast_cache.ContainsKey(id) && cast_cache[id].ContainsKey(key))
return (int) cast_cache[id][key];
int value = 0;
bool success = int.TryParse(map[id][key], out value);
if (success)
{
this.AddToCache(id, key, value);
return value;
}
else
throw new InvalidOperationException(
"Trying to obtain a non-integer value with GetInt().");
}
。
所以,我做了一些性能测试,在地图上添加了10000个整数。然后我做了一百万随机检索,有没有“强制缓存”。
没有缓存需要495(ms),缓存需要490(ms)。我还使用DateTime进行了测试,差异更显着,但比预期的要少(~750(ms)非缓存vs~500(ms)缓存)。
没有(显然)理解演员表的原则,这个操作的代价是多少以及为什么表演如此接近于从字符串“反序列化”的表现呢?
答案 0 :(得分:11)
大多数人似乎认为投射速度要快得多,因为你没有触及对象本身(你只是改变指向该对象的引用)。
你应该避免施放的一个主要原因是,当你施放时,你避免了类型安全并在你的应用程序中引入了潜在的执行时错误。我很少会考虑将其作为一个性能问题。
作为旁注,我会确保您在缓存中测试引用类型和值类型,以确保您不会因任何值类型的装箱和取消装箱而导致性能下降。
拳击的性能损失来自这样一个事实:将值类型转换为对象 涉及的不仅仅是更改引用,因为必须将值类型复制到堆中。使用盒装值类型也会取消引用引用类型,然后再将这些值从堆中复制到堆栈中。
答案 1 :(得分:2)
坚持,您在代码中称为“演员”的内容不是演员。
如果你这样做:
bool success = int.TryParse(map[id][key], out value);
这是转化,而不是演员。 强制转换将是:
value = (int) map[id][key];
或
value = map[id][key] as int;
在这种情况下哪个会失败,因为该对象确实是一个字符串,而不是一个int。
如果您有一个Dictionary<string, string>
,但您真的对将任意对象存储在其中感兴趣,我会将其设为Dictionary<string, object>
。因此,您将能够使用强制转换而不是转换,正如Andrew Hare指出的要快得多。
答案 2 :(得分:0)
如果减少字典查找次数,您的代码可能会更快地执行缓存。此外,制作缓存Dictionary<string, int>
而不是Dictionary<string, object>
将避免装箱和拆箱,这也很昂贵。
public int GetInt(string id, string key)
{
int value;
Dictionary<string, int> cache;
if (cast_cache.TryGetValue(id, out cache)
&& cache.TryGetValue(key, out value))
{
return value;
}
if (int.TryParse(map[id][key], out value))
{
this.AddToCache(id, key, value);
return value;
}
throw new InvalidOperationException("Trying to obtain a non-integer value with GetInt().");
}
答案 3 :(得分:0)
有几件事需要考虑。首先,只是让你知道正确的术语,这实际上是拆箱(你已采用值类型并将其存储为引用类型,或将其装箱。恢复到值类型是拆箱)。
其次,我打赌你的大部分代码都不在拆箱中,而是在多次调用缓存字典中:
if (cast_cache.ContainsKey(id) && cast_cache[id].ContainsKey(key))
return (int)cast_cache[id][key]
我在那里计算了5个字典遍历:cast_cache.ContainsKey(id),cast_cache [id],。ContainstKey(key),cast_cache [id]和[key]。
那太苛刻了。您可以使用聚合键来减少很多这些。而不是寻找[id] [key],将它们组合成一个对象。如果你使用try / catch跳过ContainsKey(),那将会以指数方式减少你的字典数量,并将这些查询减少到2,1(
这是一个允许你将它们组合在一起的类:
public class Vector
{
private object[] _Data;
public object this[int index]
{
get
{
return _Data[index];
}
}
public Vector(params object[] data)
{
_Data = (object[])data.Clone();
}
public override bool Equals(object obj)
{
Vector OtherVector = obj as Vector;
if (OtherVector == null)
return false;
if (OtherVector._Data.Length != _Data.Length)
return false;
for (int I = 0; I < _Data.Length; I++)
if (!_Data[I].Equals(OtherVector._Data[I]))
return false;
return true;
}
public override int GetHashCode()
{
int Result = 0;
for (int I = 0; I < _Data.Length; I++)
Result = Result ^ (_Data[I].GetHashCode() * I);
return Result;
}
}
尝试一下,看看你的速度如何
答案 4 :(得分:-2)
由于时间和空间的关系,您的示例工作得更快。如果继续缓存每种哈希类型,程序需要多少内存?