在BenchmarksGame网站的nbody程序中,我注意到
sumOfSquares()
的写法如下(在代码末尾):
// compute the sum of squares of a 3-tuple's elements
inline proc sumOfSquares((x,y,z)) {
return x**2 + y**2 + z**2;
}
我想知道上面的伪参数表示法
(x,y,z)
是否像“元组解包”一样工作,所以与下面的类似?
var (x,y,z) = {actual tuple on the caller side};
为了理解它,我尝试了以下代码,似乎实际的元组
t
通过值传递给myproc((x,y,z))
,每个组件都可以在例程中修改。
(通过 ATO 网站测试)
更具体地说,下面的 (1) 行和 (2) 行的含义相似,并且 (2) 和 (3) 行的工作原理基本相同(除了解包)。这种理解正确吗...?
var t_orig = (1.0, "hello");
var t = t_orig;
writeln("t = ", t);
var (x,y) = t; // (1)
x = 100; y = "hi";
writeln("x = ", x, " y = ", y, " t = ", t); // t -> (1.0, "hello")
ref (p,q) = t; // (1')
p = 100; q = "hi";
writeln("p = ", p, " q = ", q, " t = ", t); // t -> (100.0, "hi")
proc myproc((x, y)) // (2)
{
x += 1.0;
y += " world";
writeln("myproc(): ", (x,y));
}
proc myproc2(in u) // (3)
// proc myproc2(ref u) // t -> (2.0, "hello world")
// proc myproc2(inout u) // t -> (2.0, "hello world")
// proc myproc2(u) // (4) error: 'u' is const and cannot be modified
{
u[0] += 1.0;
u[1] += " world";
writeln("myproc2(): ", u);
}
t = t_orig;
myproc(t); // (2.0, "hello world")
writeln("t = ", t); // (1.0, "hello")
t = t_orig;
myproc2(t);
writeln("t = ", t); // depends on routines
Results from ATO:
t = (1.0, hello)
x = 100.0 y = hi t = (1.0, hello)
p = 100.0 q = hi t = (100.0, hi)
myproc(): (2.0, hello world)
t = (1.0, hello)
myproc2(): (2.0, hello world)
t = (1.0, hello)
我还想知道为什么
nbody
程序使用像(2)这样的函数形式而不是(3)。这主要是为了代码的可读性,还是可能是性能方面的考虑……?
感谢您的提问。
我想知道上面的伪参数 (x,y,z) 表示法是否像“元组解包”一样工作,与以下内容非常相似? […] ……这个理解正确吗……?
是的,我认为您的理解是正确的。 在语言规范中提供了此功能的简要文档,但可以说可以进行改进以澄清行为。
我想强调的一件事是你提到“它似乎[通过值传递]…”因为,为了支持编译器优化,我相信 Chapel 故意避免指定诸如实现如何传递元组或 at将在什么时候制作本地可修改的副本(或者即使它们......例如,如果不需要它们)。
最终,我相信 Chapel 的目的是支持像这样的去元组正式论证的论证意图(如
ref
或 inout
),这样你就可以写:
proc myproc(ref (x, y)) { … }
// or
proc myproc(inout (x, y)) { … }
并让您的程序通过本地分配对
x
和 y
修改原始元组。 如果支持,这将导致行为更像您的 (1')/ref t
或 inout t
情况。 但如果您尝试过,您可能会发现,现在不支持它:
error: intents on tuple-grouped arguments are not yet supported
我还想知道为什么nbody程序使用像(2)这样的函数形式而不是(3)。这主要是为了代码的可读性,还是可能是性能方面的考虑……?
我相信我们采取这种方法主要是出于风格而非性能的原因。 具体来说,当结果足够简洁和清晰时,我倾向于避免对元组进行索引,以避免陷入基于 1 与基于 0 的索引假设或战争。 就我个人而言,我也更喜欢在这样的上下文中将组件视为“x”、“y”和“z”,而不是“t[0]”、“t1”、“t[2]”。
也就是说,由于 CLBG 支持通过紧凑性比较代码,因此看看是否重写如下会很有趣:
inline proc sumOfSquares(in t) {
return t[0]**2 + t[1]**2 + t[2]**2;
}
使用网站的指标生成更紧凑的代码(其中涉及删除注释和不必要的空格,然后对结果进行 gzip 压缩)。
就性能而言,该过程被声明为
inline
的事实将导致其主体在调用点处内联,这使我期望后端编译器将优化消除两种方法之间的任何实现差异。 也就是说,我承认最近没有比较过它们(如果有的话)。 如果您发现它们的表现不同,那会很有趣。 如果是这样的话,我不知道有什么理由不能改进编译器以使它们具有相同的性能。 当然,我们的目标是让像这样的细微风格差异表现相同。