铸造成本?

时间:2009-08-17 18:40:25

标签: c# caching casting

我有{/ 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)缓存)。

没有(显然)理解演员表的原则,这个操作的代价是多少以及为什么表演如此接近于从字符串“反序列化”的表现呢?

5 个答案:

答案 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)

由于时间和空间的关系,您的示例工作得更快。如果继续缓存每种哈希类型,程序需要多少内存?