【问题标题】:Most appropriate data structure for dynamic languages field access最适合动态语言字段访问的数据结构
【发布时间】:2011-06-14 07:40:47
【问题描述】:

我正在实现一种将编译为 C# 的动态语言,并且它正在实现自己的反射 API(.NET 太慢,并且 DLR 仅限于更新和资源丰富的实现)。

为此,我实现了一个简单的 .GetField(string f) 和 .SetField(string f, object val) 接口。直到最近,实现只是切换所有可能的字段字符串值并进行相应的操作。 此外,这种动态语言可以定义匿名对象。对于那些匿名对象,一开始我实现了一个简单的哈希算法。

到现在为止,我正在寻找优化语言动态部分的方法,并且我发现匿名对象的哈希算法会有点过头了。这是因为对象通常很小。我会说这些对象通常包含 2 或 3 个字段。很少,它们会包含超过 15 个字段。与我测试它们之间的相等性相比,实际散列字符串并执行查找将花费更多时间。 (这未经测试,只是理论上的)。

我做的第一件事是——在编译时——为每个匿名对象声明创建一个红黑树,并将它放在一个数组上,以便对象可以以非常优化的方式查找它。

不过,如果这是最好的方法,我仍然存在分歧。我可以选择一个完美的散列函数。更激进的是,我正在考虑放弃对字符串的需求,并实际使用 2 个 long 的结构。

这两个 long 将被编码为每个支持 10 个字符 (A-za-z0-9_),这主要是对字段大小的良好预测。对于大于此的字段,还会提供一个特殊的函数(较慢)接收字符串。

结果将是字符串将被内联(而不是引用),并且它们的比较将与长比较一样便宜。

无论如何,要找到关于这种优化的好信息有点困难,因为这通常是在 vm 级别上考虑的,而不是静态语言编译实现。

是否有人对处理动态调用的最佳数据结构有任何想法或提示?

编辑: 现在,我真的将字符串作为长表示和线性二叉树查找。

【问题讨论】:

    标签: optimization data-structures dynamic hash tree


    【解决方案1】:

    我不知道这是否有帮助,但我会把它扔掉以防万一;

    如果这是编译为 C#,您是否知道编译时的完整字段列表?所以作为一个想法,如果你的代码是

    // dynamic
    myObject.foo = "some value";
    myObject.bar = 32;
    

    那么在解析过程中,你的符号表可以为每个字段名构建一个int;

    // parsing code
    symbols[0] == "foo"
    symbols[1] == "bar"
    

    然后使用数组或列表生成代码;

    // generated c#
    runtimeObject[0] = "some value"; // assign myobject.foo
    runtimeObject[1] = 32; // assign myobject.bar
    

    并将反射构建为一个单独的数组;

    runtimeObject.FieldNames[0] == "foo"; // Dictionary<int, string>
    runtimeObject.FieldIds["foo"] === 0;  // Dictionary<string, int>
    

    正如我所说,扔掉是希望它有用。不知道会不会!

    【讨论】:

    • 首先感谢您的回答!一开始我真的有想过,但它似乎会在加载的模块上产生冲突,我对这个解决方案不是很满意。在查看了 nekovm 的反射是如何实现的之后,我重新审视了这个问题,并意识到它非常接近你的建议。 Neko 有一个散列函数,负责散列所有动态字段。它将在编译时散列所有已知字段和字段访问,并且相同的散列函数在运行时起作用
    • 我将在 C# 上实现一个大致相似的版本,并且冲突将回退到字符串查找。碰撞将非常罕见,所以我认为这是最佳配置!谢谢
    【解决方案2】:

    由于您可能会重复使用相同的字段和方法名称,因此string interning 之类的名称可以很好地为您的哈希表快速生成键。它还可以使字符串相等性比较恒定时间。

    【讨论】:

    • 我已经考虑过了,但是(没有进行基准测试,抱歉),我听说 String.IsInterned(我一定是打错了 :))和 String.Intern 操作很慢。而且由于我们在编译时并不总是知道要访问的字符串文字,因此我可能必须在每个字段访问时都对字符串进行实习,这甚至可能使事情变得更糟!当然,如果我保证(像 Lua 一样)所有字符串总是被保留,那就太好了。但由于它会与原生 .net 代码互操作,我真的无法做出这样的假设!
    • 我在想,也许我真的可以在我实现的编译器上实习所有字符串。在执行基本的 scape 分析时只实习所有字符串,并实习从外部函数返回的所有字符串。问题是:值得吗?我不知道字符串实习在 .net 上的表现如何,但应该有理由将其视为内存优化而不是性能
    【解决方案3】:

    对于这么小的数据集(预计上限为 15),我认为几乎任何散列都会比树甚至列表查找更昂贵,但这实际上取决于您的散列算法。

    如果您想使用字典/散列,那么您需要确保用于键的对象快速返回散列码(可能是构建一次的单个常量散列码)。如果您可以防止对象内部发生冲突(听起来很可行),那么您将获得哈希表的速度和可扩展性(适用于任何实际对象/类大小)。

    想到的是 Ruby 的符号和消息传递。我相信 Ruby 的符号只是一个内存引用的常量。所以比较是不变的,它们非常精简,你可以使用变量之类的符号(我对此有点模糊,这台机器上没有 Ruby 解释器)。 Ruby 的方法“调用”真正变成了消息传递。类似于:obj.func(arg) 变成 obj.send(:func, arg)(“:func”是符号)。我想这个符号使得在对象内部查找消息处理程序(我将称之为)非常有效,因为它的哈希码很可能不需要像大多数对象一样计算。

    也许类似的事情可以在 .NET 中完成。

    【讨论】:

      猜你喜欢
      • 2010-10-16
      • 1970-01-01
      • 2012-10-19
      • 2021-12-06
      • 1970-01-01
      • 2011-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多