了解APL的内在产品

时间:2014-06-10 13:14:37

标签: matrix matrix-multiplication apl dyalog

以下是掌握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程序员通常应尽可能使用内部产品以及外部产品。这是对的吗?

使用内部产品可以提高性能吗?当我使用内部产品(在较低级别)时会发生什么?第一个解决方案是否出现在 horrible 之下,只是因为它没有以正确的方式使用APL语法,或者它实际上是否有更差的性能?

我知道几乎没有问题,但我希望我一般都会问 内/外产品如何工作以及APL程序员何时应该使用。< / p>

3 个答案:

答案 0 :(得分:3)

  

APL程序员通常应尽可能使用内部产品和外部产品。这是对的吗?

这完全取决于APL程序员和手头的任务,但如果某些东西使APL代码更简洁高效,我不明白为什么程序员不会选择它。

在这种特殊情况下,60⊥HMS比内在产品更简洁有效。

  

使用内在产品会带来性能提升的例子吗?

在面向数组的编程中,通常可以通过一次性操作来实现性能提升。 大多数APL函数都是隐式循环---它们的实现使用计数器,对它的限制和增量步骤。 你的代码越短越好,因为不仅它更容易保持在一个人的头脑中,它也更有效,因为解释器必须对数据进行更少的传递。 一些实现执行循环融合以尝试减少此开销。 有些具有成语识别 ---在解释器中,某些曲线组合是特殊的。一次完成操作还允许解释器进行巧妙的优化,例如使用SSE指令集或GPU。

回到内部产品,让我们以A f.g B为例,其中AB是向量,并了解如何应用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% ⎕⎕⎕⎕⎕⎕⎕

答案 1 :(得分:3)

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

MBaas是正确的,因为在这个例子中+ /稍微好于+。×

我们的一般建议是:以最适合工作的语言使用构造,最终实现将迎头赶上。

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

此致

Vince,Dyalog支持

答案 2 :(得分:2)

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

现在,看看实践中的事情:

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

  • 基准测试“(3600×HMS [1])+(60×HMS [2])+ HMS [3]”,重复= 100000                     EXP CPU(平均):0.001558503836 经历时间:0.001618446292

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

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

这是一个很大的区别 - 如果你重复它足够的时间来衡量;-) 但当然,对于更大的数据集,优势变得更加明显! 让我们看一下3变量:

`] 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 ...; - )