以下是掌握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>
答案 0 :(得分:3)
APL程序员通常应尽可能使用内部产品和外部产品。这是对的吗?
这完全取决于APL程序员和手头的任务,但如果某些东西使APL代码更简洁高效,我不明白为什么程序员不会选择它。
在这种特殊情况下,60⊥HMS
比内在产品更简洁有效。
使用内在产品会带来性能提升的例子吗?
在面向数组的编程中,通常可以通过一次性操作来实现性能提升。 大多数APL函数都是隐式循环---它们的实现使用计数器,对它的限制和增量步骤。 你的代码越短越好,因为不仅它更容易保持在一个人的头脑中,它也更有效,因为解释器必须对数据进行更少的传递。 一些实现执行循环融合以尝试减少此开销。 有些具有成语识别 ---在解释器中,某些曲线组合是特殊的。一次完成操作还允许解释器进行巧妙的优化,例如使用SSE指令集或GPU。
回到内部产品,让我们以A f.g B
为例,其中A
和B
是向量,并了解如何应用f
和g
(在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
您可以从上面看到,f
和g
的来电是交错的。解释器使用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
这里没有区别,但是再次 - 对于“真实数据”(更大的数组),您应该看到更明显的差异。我想一个简单的解释是内部产品就像解释器的一个“声明”,而第一个变体有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 ...; - )