【问题标题】:Netlogo slow to get table value from indexed turtleNetlogo 从索引乌龟获取表值很慢
【发布时间】:2020-09-11 04:52:06
【问题描述】:

我正在构建一个包含社交传播组件的 Netlogo 模型,并且在使用 Table 扩展时遇到了一个令人惊讶的性能问题。

对于某些上下文:在模型中,每个代理都有一个态度表,其中包含一个字符串键(例如“Environment”)和一个从 -1 到 1 的浮点值。每个滴答声,代理都会根据他们的态度更新他们的态度联系人和每个联系人的权重。为了制作原型,我为每个联系人使用了简单的更新规则:

a(t) = a(t-1) + w * [b(t-1) - a(t-1)]

其中a是代理的态度,b是接触的态度,w是权重。每个代理有 5-10 个联系人。

我通过两种方式实现了这一点。第一个使用表现不错的链接。但是,当我扩大规模时,它的内存占用似乎变得相当大。由于网络不是动态的,我正在尝试将其实现为一个表,其中键是联系人海龟 ID,值是权重(严格来说,我创建了一个名为“联系人”的海龟 ID 列表和一个相应权重表以避免重新创建每次迭代时的列表)。对于许多代理来说,这似乎使用少得多的内存,但出乎意料地慢了一个数量级。

这是基于网络的更新的 sn-p:

ask my-in-links [
   let neighborAttitude table:get [attitudes] of other-end "Environment"
   let myAttitude table:get [attitudes] of myself "Environment"
   let influence weight * (neighborAttitude - myAttitude)
   set myAttitude myAttitude + influence
   table:put [attitudes] of myself "Environment" myAttitude
  ]

还有基于表格的版本:

  foreach (contacts) [ i ->
    let myAttitude table:get attitudes "Environment"
    let neighborAttitude table:get [attitudes] of homeowner i "Environment"
    let w table:get contactWeights i
    let influence w * (neighborAttitude - myAttitude)
    set myAttitude myAttitude + influence
    table:put attitudes "Environment" myAttitude
  ]

从一些测试看来,获得邻居态度会导致大幅放缓。如果我注释掉那行,从代理的态度表中获取和放置似乎与基于网络的块一样快。所以它似乎正在寻找另一只乌龟(“房主”)。

在 NetLogo 中通过 id 查找海龟/代理是否存在本质上非常缓慢的问题?这似乎应该基本上是免费的,但我不知道引擎盖下的数据结构是什么。

【问题讨论】:

    标签: performance netlogo


    【解决方案1】:

    这段代码非常不像 NetLogo,所以我在解析它时遇到了一些麻烦,我的评论/答案可能完全没有抓住重点。

    我完全不明白table 在这些行中的用法:

    ask my-in-links [
       let neighborAttitude table:get [attitudes] of other-end "Environment"
    

    您从ask my-in-links 开始。大概这是在ask turtles 或类似的块内。所以运行代码首先选择一个海龟,假设海龟有5个链接到其他海龟。它点击这段代码并遍历这 5 个链接中的每一个,并创建一个包含这 5 个值的表,这些值以某种方式存储在一个带有键 [attitudes] of other-end "Environment" 的表中。

    假设你有链接(你必须能够使用ask my-in-links),那么一个更类似于 NetLogo 的方式如下所示。注意这是一个完整的模型,你可以把它放在一个空的NetLogo模型中运行它。

    turtles-own
    [ myAttitude
      myUpdatedAttitude
    ]
    
    links-own
    [ wgt
    ]
    
    to setup
      clear-all
      create-turtles 20
      [ setxy random-xcor random-ycor
        set myAttitude -1 + random-float 2
      ]
      ; create a network
      repeat 50
      [ ask one-of turtles
        [ create-link-to one-of other turtles
          [ set wgt random-float 1 ]
        ]
      ]
      reset-ticks
    end
    
    to go
      type "average attitude: " print mean [myAttitude] of turtles
      let w 0.4
      ; calculate next value and store until all calculated
      ask turtles with [any? in-link-neighbors]
      [ let wtd-diffs [([myAttitude] of other-end - [myAttitude] of myself) * wgt] of my-in-links
        set myUpdatedAttitude (1 - w) * myAttitude + w * sum wtd-diffs 
      ]
      ; make the change
      ask turtles [ set myAttitude myUpdatedAttitude ]
      tick
    end
    

    对于每个海龟,此代码依次构建一个海龟与其受其影响的海龟之间姿态值的加权差的列表。因此,如果它有 5 个in-link-neighbors,那么 wtd-diffs 列表将有 5 个值。下一行计算总影响,将其存储在不同的值中,因此所有计算都在当前值上完成。请注意,我没有完全使用您的方程式,因为我不确定您是如何防止它不受控制地增长的。

    我知道这并不能回答您关于表性能的问题,但我希望这种方法可以解决您的所有性能问题。

    【讨论】:

    • 谢谢,@JenB。我意识到我的示例代码可能比必要的更复杂,因为使用表扩展来存储态度是无关紧要的。澄清一下,该块不是创建一个表来存储值。使用表格扩展,语法table:get <table> <key> of <turtle> 引用给定海龟的表格值。等效为let neighborAttitude [...] of other-end。这里的解决方案也略有不同,因为我对每个联系人都有一个权重,可以在 0 到 1 之间。我可以将你的方法与 map reduce 一起使用。
    • 下面没有表格的简化示例。这会产生与平均差异(抱歉,忘记了 cmets 的限制!)不同的东西(我很确定):询问 my-in-links [让我自己的 myAttitude [环境] 让影响权重 *(其他 [环境] -end - myAttitude) 设置我自己的 [环境] myAttitude + 影响]
    • 我还是完全不懂表格的使用。通过使权重成为链接的属性,每个联系人的不同权重很简单,将进行修改。您有每个联系人的链接,该表实际存储的是什么?
    • 是的,影响权重是链接的一个属性。该表用于存储态度。碰巧有几个,其中标签(例如“环境”)是关键,价值是态度。它们每个都可能是单独的属性并且问题将持续存在,因为实际问题(请参阅我自己的答案)似乎与线的成本有关通过每一个,直到它匹配。如果我以所有海龟的 id 为键制作一张表,结果会非常快。
    • 我将代码更改为拥有 20000 个代理,每个代理有 10 个影响。所有 20000 只海龟的每次迭代大约需要 20 秒。你期待什么表现?
    【解决方案2】:

    在提交此文件后,我立即尝试了一个实验来证实我的怀疑,并且我认为得出了一个可以接受的解决方案。事实证明,简单地让海龟查找“turtle i”是很慢的,并且随着代理数量的增加而变得越来越慢。

    猜测of turtle i Netlogo 的作用相当于of turtles with [who = i],并且必须遍历所有海龟的列表,直到找到匹配的 ID。因此,作为测试,我再次使用表扩展名手动创建了一个包含所有海龟的表:

    globals [turtleTable]
    ...
    to initialize-turtleTable
      set turtleTabletable:make
      ask turtles [
        table:put turtleTable who self
      ]
    end
    

    那么你可以说of table:get turtleTable i,而不是of turtle i。这是稍微繁琐的语法,但可以用记者清理。与完全退出并重新启动 NetLogo 相比,这最终按最初预期执行,与使用链接大致相当,但大约 25,000 个代理使用了近一半的内存(5GB)。

    【讨论】:

    • turtle i 是一种高效的恒定时间查找。或者至少是在我为 NetLogo 工作的 13 年中——我不能确定从那以后它没有倒退,但如果它倒退了,我会非常惊讶。我认为您应该首先考虑其他可能的解释。
    • 我当然预料到了这一点,所以我一直在尝试更多地挖掘为什么我所做的改变在这里加快了速度。如果除了let neighborAttitude [anyVariable] of homeowner i 之外我在该块中什么都没有,它绝对会崩溃,而用表格查找替换它会快十倍。但是,刚才在一个大大简化的测试模型中隔离了这一点,并且我可以在这两种方法之间进行更改,它们的性能似乎确实大致相同。哪个...是我所期望的,因为浏览代码海龟集似乎是数组。
    • 啊,@SethTisue。我在我的测试模型中发现了一些东西。 turtle i 是高效的,甚至比表还要快一些。但是,当我将代理创建为自定义品种(例如金枪鱼)然后尝试使用 tuna i 时,性能下降了大约一个数量级。我没想到breedturtle 不同。有什么理由吗?
    • 确认:品种的查找时间,即of tuna i随着索引的增加而增加。我创建了 1,000 只金枪鱼海龟,每次滴答都会重置计时器并询问每只海龟 let j [someVariable] of tuna i 其中 i 从零开始并递增。我绘制了计时器。金枪鱼 0 的查找时间大约为零,但至少呈线性增长。 ... of turtle i 按预期显示恒定时间。
    • @SethTisue 刚刚提交了一份报告以及一个测试程序和屏幕截图,我们将看看它是否可复制或者我是否做错了什么:github.com/NetLogo/NetLogo/issues/1856
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多