5

以下是Mastering Dyalog APL一书的摘录,来自内部产品一章:

HMS is a variable which contains duration in Hours, Minutes, and Seconds: HMS ← 3 44 29 Chapter J – Operators 397

We would like to convert it into seconds. We shall see 3 methods just now, and a 4th
 method 
will be given in another chapter. 
    A horrible solution                        (3600×HMS[1]) + (60×HMS[2]) + HMS[3] 
    A good APL solution                        +/ 3600 60 1 × HMS 
    An excellent solution with Inner Product   3600 60 1 +.× HMS 

然后它说第二个和第三个解决方案在键入的字符数和性能方面是等效的。

据我了解,APL 程序员通常应该尽可能使用Inner Product以及Outer Product。那是对的吗?

你能举一个例子,当使用内积会导致性能提升?当我使用内部产品(在较低级别)时,究竟会发生什么?下面介绍的第一个解决方案是否很糟糕,仅仅是因为它没有以正确的方式使用 APL 语法,还是实际上性能更差?

我知道有几个问题,但我想问的一般是内部/外部产品是如何工作的,以及 APL 程序员应该在什么时候使用它们

4

3 回答 3

6

我们已经完成了优化 +/ 和 +.× 的工作。

MBaas 是正确的,因为在这种情况下 +/ 恰好比 +.×

我们的一般建议是:使用最适合工作的语言中的构造,最终实现会赶上来。

“可怕”的解决方案被认为是不好的,因为它不使用数组思维。

问候,

文斯,Dyalog 支持

于 2014-06-13T12:16:05.283 回答
5

APL 程序员通常应该尽可能使用内积和外积。那是对的吗?

这真的取决于 APL 程序员和手头的任务,但是如果有什么东西可以让 APL 代码更加简洁和高效,我不明白程序员为什么不选择它。

在这种特殊情况下60⊥HMS,它比内积更加简洁和高效。

你能举一个例子,当使用内积会导致性能提升?

在面向数组的编程中,性能提升是通过一次性完成来实现的。大多数 APL 函数都是隐式循环——它们的实现使用一个计数器、一个限制和一个增量步骤。你的代码越短越好,因为它不仅更容易记住,而且效率也更高,因为解释器必须对数据进行更少的传递。一些实现进行循环融合以试图减少这种开销。有些具有成语识别功能——某些曲线组合在解释器中是特殊情况。一次性完成还允许解释器进行巧妙的优化,例如使用 SSE 指令集或 GPU。

回到内积,让我们以A f.g BwhereABare 向量为例,看看如何应用fg(在 Dyalog 中):

      f←{⎕←(⍕⍺),' f ',⍕⍵ ⋄ ⍺+⍵}
      g←{⎕←(⍕⍺),' g ',⍕⍵ ⋄ ⍺×⍵}
      0 1 2 3 4 f.g 5 6 7 8 9
4 g 9
3 g 8
24 f 36
2 g 7
14 f 60
1 g 6
6 f 74
0 g 5
0 f 80
80

从上面可以看出,调用fg是交错的。解释器在一次通过中同时执行苹果f和减少操作g,避免创建临时数组,就像f/ A g B会做的那样。

另一个例子: http: //archive.vector.org.uk/art10500200

您可以自己测试不同解决方案的性能,看看哪一个效果最好:

      )copy dfns.dws cmpx
      ⍝ or: ")copy dfns cmpx" if you are using Windows
      HMS ← 3 44 29
      cmpx '(3600×HMS[1]) + (60×HMS[2]) + HMS[3]' '+/ 3600 60 1 × HMS' '3600 60 1 +.× HMS' '60⊥HMS'
  (3600×HMS[1]) + (60×HMS[2]) + HMS[3] → 2.7E¯6 |   0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
  +/ 3600 60 1 × HMS                   → 9.3E¯7 | -66% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
  3600 60 1 +.× HMS                    → 8.9E¯7 | -68% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕
  60⊥HMS                               → 4.8E¯7 | -83% ⎕⎕⎕⎕⎕⎕⎕
于 2014-06-11T14:48:12.333 回答
4

泛化的问题是它们可能不正确,但根据经验,我会说使用内部和外部产品将有利于可读性和性能;-)

现在,看看实际情况:

` ]performance.RunTime (3600×HMS[1])+(60×HMS[2])+HMS[3] -repeat=100000

  • 基准测试“(3600×HMS[1])+(60×HMS[2])+HMS[3]”,repeat=100000 Exp CPU (avg): 0.001558503836 Elapsed: 0.001618446292

    ]performance.RunTime '+/ 3600 60 1 × HMS' -repeat=100000

  • 基准测试“+/ 3600 60 1 × HMS”,重复 = 100000 Exp CPU(平均):0.0004698496481 经过:0.0004698496481 `

那是一个很大的区别——如果你重复它足够多的时间来衡量;-) 当然,随着数据集的增加,优势会变得更加明显!让我们也看看 3variant:

` ]performance.RunTime '3600 60 1 +.× HMS' -repeat=100000

  • 基准测试“3600 60 1 +.× HMS”,重复 = 100000 Exp CPU(平均):0.0004698496481 经过:0.000439859245
    `

这里没有区别,但同样 - 使用“真实数据”(更大的数组),您应该会看到更清晰的区别。我认为一个简单的解释是,内积就像一个解释器的“语句”,而第一个变体有 3 个单次乘法、索引并且需要考虑优先级(括号)然后总结那个向量,这听起来像是很多汗 ;-) 第二条语句只有一个乘法(对于一个向量),所以它已经消除了几个步骤,并且内积使解释器可以结合它的一些内部工作来更快地完成他的工作。

现在有一个惊喜:v1←(10000/3600 60 1) ⋄v2← 10000/HMS ]performance.RunTime '+/v1 × v2' 'v1 +.× v2' -repeat=100000 -compare

  +/v1 × v2 → 6.0E¯5 |  0% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕   
  v1 +.× v2 → 6.3E¯5 | +5% ⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕⎕ 

我预计更大的参数将有助于使最后一个表达式的性能优势更加明显——但实际上 #2 赢了。也许 Dyalog 优化案例 #2 多于 #3... ;-)

于 2014-06-11T11:27:50.490 回答