- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在运行GHC编译的程序时,我经常看到在GC中花费了大量的周期。
这些数字往往比我的JVM经验所建议的高几个数量级。特别是,GC“复制”的字节数似乎比我正在计算的数据量大得多。
非严格语言和严格语言之间的这种区别是根本的吗?
最佳答案
tl;博士: JVM在堆栈帧中执行的大多数工作,GHC在堆中执行。如果您想将GHC堆/ GC统计信息与JVM等效项进行比较,则确实需要考虑JVM在堆栈上推参数或在堆栈帧之间复制返回值所花费的字节/周期的某些部分。
长版:
面向JVM的语言通常利用其调用堆栈。每个被调用的方法都有一个活动的堆栈框架,其中包括传递给它的参数的存储,其他局部变量和临时结果,以及用于“操作数堆栈”的空间,该操作数用于将参数传递给它并从其调用的其他方法接收结果。
作为一个简单的示例,如果Haskell代码为:
bar :: Int -> Int -> Int
bar a b = a * b
foo :: Int -> Int -> Int -> Int
foo x y z = let u = bar y z in x + u
public static int bar(int, int);
Code:
stack=2, locals=2, args_size=2
0: iload_0 // push a
1: iload_1 // push b
2: imul // multiply and push result
3: ireturn // pop result and return it
public static int foo(int, int, int);
Code:
stack=2, locals=4, args_size=3
0: iload_1 // push y
1: iload_2 // push z
2: invokestatic bar // call bar, pushing result
5: istore_3 // pop and save to "u"
6: iload_0 // push x
7: iload_3 // push u
8: iadd // add and push result
9: ireturn // pop result and return it
imul
的内置基元和诸如
bar
的用户定义方法的调用涉及将参数值从本地存储复制/推入操作数堆栈(使用
iload
指令),然后调用该基元或方法。然后需要将返回值保存/弹出到本地存储中(使用
istore
)或使用
ireturn
返回给调用者;有时,返回值可以留在堆栈上,用作另一个方法调用的操作数。同样,虽然在字节码中没有明确指出,但
ireturn
指令涉及从被调用方的操作数堆栈到调用方的操作数堆栈的副本。当然,在实际的JVM实现中,可能可以进行各种优化来减少复制。
foo
进行计算时,例如:
some_caller t = foo (1+3) (2+4) t + 1
iconst_1
iconst_3
iadd // put 1+3 on the stack
iconst_2
iconst_4
iadd // put 2+4 on the stack
iload_0 // put t on the stack
invokestatic foo
iconst 1
iadd
ireturn
foo
并将其参数压入堆栈,并弹出结果进行进一步处理。
foo [x, y, z] =
u = new THUNK(sat_u) // thunk, 32 bytes on heap
jump: (+) x u
sat_u [] = // saturated closure for "bar y z"
push UPDATE(sat_u) // update frame, 16 bytes on stack
jump: bar y z
bar [a, b] =
jump: (*) a b
(+)
和
(*)
“基元”的调用/跳转比我所指出的要复杂得多,原因在于所涉及的类型类。例如,跳转到
(+)
看起来更像:
push CONTINUATION(\f -> f x u) // continuation, 24 bytes on stack
jump: (+) dNumInt // get the right (+) from typeclass instance
-O2
,GHC会优化此更复杂的调用,但也会优化此示例中有趣的所有其他事情,因此,为了进行论证,让我们假设上面的伪代码是准确的。
foo
之前,它没有多大用处。对于上面的
some_caller
示例,调用
foo
的代码部分将类似于:
some_caller [t] =
...
foocall = new THUNK(sat_foocall) // thunk, 24 bytes on heap
...
sat_foocall [] = // saturated closure for "foo (1+3) (2+4) t"
...
v = new THUNK(sat_v) // thunk "1+3", 16 bytes on heap
w = new THUNK(sat_w) // thunk "2+4", 16 bytes on heap
push UPDATE(sat_foocall) // update frame, 16 bytes on stack
jump: foo sat_v sat_w t
sat_v [] = ...
sat_w [] = ...
sat_u
的定义中考虑对
foo
进行修改。它是32字节/ 4个字,内容如下:
// THUNK(sat_u)
word 0: ptr to sat_u info table/code
1: space for return value
// variables we closed over:
2: ptr to "y"
3: ptr to "z"
0: iload_1 // push y
1: iload_2 // push z
2: invokestatic bar // call bar, pushing result
5: istore_3 // pop and save to "u"
y
和
z
推入操作数堆栈,而是将它们加载到堆分配的thunk中。与其将结果从操作数堆栈弹出到我们的堆栈帧的本地存储中,管理堆栈帧和返回地址,不如将结果留在thunk中,并在将控制权转移到
bar
之前将一个16字节的更新帧压入堆栈。
foo
中对
some_caller
的调用中,我们没有在堆栈上推送常量并在堆栈上调用基元来将结果推送到堆栈中,而是通过在堆上创建thunk来评估参数子表达式,每个thunk均包含指向信息表/代码的指针用于调用这些参数的原语和返回值的空间;更新框架替换了JVM版本中隐含的堆栈簿记和结果复制。
Ints
时,事情将会变得更加复杂。但是,这只是为了说明一点,即直接比较GHC和JVM之间的堆使用情况和GC周期并没有多大意义。当然,由于JVM和GHC方法在根本上太不同了,因此实际上似乎无法进行精确的核算,并且证明将是真实的性能。至少,要逐一比较GHC堆使用情况和GC统计信息,这需要考虑JVM在操作数堆栈之间推送,弹出和复制值所花费的部分周期。特别是,至少JVM
return
指令的一部分应计入GHC的“已复制字节”。
关于haskell - Haskell是否有内在的“垃圾成本”?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55562754/
我有一个 if 语句,如下所示 if (not(fullpath.lower().endswith(".pdf")) or not (fullpath.lower().endswith(tup
然而,在 PHP 中,可以: only appears if $foo is true. only appears if $foo is false. 在 Javascript 中,能否在一个脚
XML有很多好处。它既是机器可读的,也是人类可读的,它具有标准化的格式,并且用途广泛。 它也有一些缺点。它是冗长的,不是传输大量数据的非常有效的方法。 XML最有用的方面之一是模式语言。使用模式,您可
由于长期使用 SQL2000,我并没有真正深入了解公用表表达式。 我给出的答案here (#4025380)和 here (#4018793)违背了潮流,因为他们没有使用 CTE。 我很欣赏它们对于递
我有一个应用程序: void deleteObj(id){ MyObj obj = getObjById(id); if (obj == null) { throw n
我的代码如下。可能我以类似的方式多次使用它,即简单地说,我正在以这种方式管理 session 和事务: List users= null; try{ sess
在开发J2EE Web应用程序时,我通常会按以下方式组织我的包结构 com.jameselsey.. 控制器-控制器/操作转到此处 服务-事务服务类,由控制器调用 域-应用程序使用的我的域类/对象 D
这更多是出于好奇而不是任何重要问题,但我只是想知道 memmove 中的以下片段文档: Copying takes place as if an intermediate buffer were us
路径压缩涉及将根指定为路径上每个节点的新父节点——这可能会降低根的等级,并可能降低路径上所有节点的等级。有办法解决这个问题吗?有必要处理这个吗?或者,也许可以将等级视为树高的上限而不是确切的高度? 谢
我有两个类,A 和 B。A 是 B 的父类,我有一个函数接收指向 A 类型类的指针,检查它是否也是 B 类型,如果是将调用另一个函数,该函数接受一个指向类型 B 的类的指针。当函数调用另一个函数时,我
有没有办法让 valgrind 使用多个处理器? 我正在使用 valgrind 的 callgrind 进行一些瓶颈分析,并注意到我的应用程序中的资源使用行为与在 valgrind/callgrind
假设我们要使用 ReaderT [(a,b)]超过 Maybe monad,然后我们想在列表中进行查找。 现在,一个简单且不常见的方法是: 第一种可能性 find a = ReaderT (looku
我的代码似乎有问题。我需要说的是: if ( $('html').attr('lang').val() == 'fr-FR' ) { // do this } else { // do
根据this文章(2018 年 4 月)AKS 在可用性集中运行时能够跨故障域智能放置 Pod,但尚不考虑更新域。很快就会使用更新域将 Pod 放入 AKS 中吗? 最佳答案 当您设置集群时,它已经自
course | section | type comart2 : bsit201 : lec comart2 :
我正在开发自己的 SDK,而这又依赖于某些第 3 方 SDK。例如 - OkHttp。 我应该将 OkHttp 添加到我的 build.gradle 中,还是让我的 SDK 用户包含它?在这种情况下,
随着 Rust 越来越充实,我对它的兴趣开始激起。我喜欢它支持代数数据类型,尤其是那些匹配的事实,但是对其他功能习语有什么想法吗? 例如标准库中是否有标准过滤器/映射/归约函数的集合,更重要的是,您能
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎与 help center 中定义的范围内的编程无关。 . 关闭 9 年前。 Improve
我一直在研究 PHP 中的对象。我见过的所有示例甚至在它们自己的对象上都使用了对象构造函数。 PHP 会强制您这样做吗?如果是,为什么? 例如: firstname = $firstname;
...比关联数组? 关联数组会占用更多内存吗? $arr = array(1, 1, 1); $arr[10] = 1; $arr[] = 1; // <- index is 11; does the
我是一名优秀的程序员,十分优秀!