Skip to content

Commit 4f95ea5

Browse files
committed
meta: end of phase 0
1 parent b98acdf commit 4f95ea5

File tree

8 files changed

+113
-16
lines changed

8 files changed

+113
-16
lines changed

bib/book.bib

+29
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,15 @@ @misc{selectiveLambdaLifting
261261
copyright = {arXiv.org perpetual, non-exclusive license}
262262
}
263263

264+
@misc{callArityVsDemandAnalysis,
265+
title = {Call Arity vs. Demand Analysis},
266+
year = {2017},
267+
month = aug,
268+
author = {Sebastian Graf},
269+
school = {Karlsruher Institut f{\"u}r Technologie (KIT)},
270+
institution = {IPD Snelting},
271+
}
272+
264273
@inproceedings{fastCurry,
265274
author = {Marlow, Simon and Jones, Simon Peyton},
266275
title = {Making a Fast Curry: Push/Enter vs. Eval/Apply for Higher-Order Languages},
@@ -277,3 +286,23 @@ @inproceedings{fastCurry
277286
location = {Snow Bird, UT, USA},
278287
series = {ICFP '04}
279288
}
289+
290+
@article{hoCardinality,
291+
author = {Sergey, Ilya and Vytiniotis, Dimitrios and Peyton Jones, Simon},
292+
title = {Modular, Higher-Order Cardinality Analysis in Theory and Practice},
293+
year = {2014},
294+
issue_date = {January 2014},
295+
publisher = {Association for Computing Machinery},
296+
address = {New York, NY, USA},
297+
volume = {49},
298+
number = {1},
299+
issn = {0362-1340},
300+
url = {https://doi.org/10.1145/2578855.2535861},
301+
doi = {10.1145/2578855.2535861},
302+
abstract = {Since the mid '80s, compiler writers for functional languages (especially lazy ones) have been writing papers about identifying and exploiting thunks and lambdas that are used only once. However it has proved difficult to achieve both power and simplicity in practice. We describe a new, modular analysis for a higher-order language, which is both simple and effective, and present measurements of its use in a full-scale, state of the art optimising compiler. The analysis finds many single-entry thunks and one-shot lambdas and enables a number of program optimisations.},
303+
journal = {SIGPLAN Not.},
304+
month = {jan},
305+
pages = {335–347},
306+
numpages = {13},
307+
keywords = {lazy evaluation, haskell, functional programming languages, compilers, static analysis, cardinality analysis, program optimisation, operational semantics, types and effects, thunks}
308+
}

contents.rst

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Haskell Optimization Handbook
1616
src/Preliminaries/index
1717
src/Measurement_Observation/index
1818
src/Optimizations/index
19+
src/Case_Studies/index
1920

2021

2122
Indices and tables

src/Case_Studies/index.rst

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Case Studies
2+
============
3+
4+
.. toctree::
5+
:maxdepth: 1
6+
:name: Case_Studies
7+
8+
sbv_572
9+
sbv_642

src/Case_Studies/sbv_572.rst

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.. _SBV572:
2+
3+
Impact of Seq Removal on SBV's Internal Cache
4+
=============================================

src/Case_Studies/sbv_642.rst

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.. _SBV642:
2+
3+
SBV and the Bizarre GHC Regression
4+
==================================

src/Optimizations/GHC_opt/lambda_lifting.rst

+7-7
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ when *not* to lambda lift in `Note [When to lift]
8787

8888
GHC does not lambda lift:
8989

90-
#. A :term:`Top-level binding`. By definition these cannot be lifted.
90+
#. A :term:`top-level` binding. By definition these cannot be lifted.
9191
#. :term:`Thunk` and Data Constructors. Lifting either of these would destroy
9292
sharing.
9393
#. :term:`Join Point` because there is no lifting possible in a join point.
@@ -103,8 +103,8 @@ GHC does not lambda lift:
103103
#. Any function whose lifted form will result in *closure grawth*. Closure
104104
growth occurs when formerly free variables, that are now additional
105105
arguments, did not previously occur in the closure, thereby increasing
106-
allocations. This is especially bad for :term:`multi-shot` lambdas which will
107-
allocate many times.
106+
allocations. This is especially bad for any :term:`multi-shot lambda`, which
107+
will allocate many times.
108108

109109

110110
Observing the Effect of Lambda Lifting
@@ -117,7 +117,7 @@ late lambda lifting with the flags ``-f-stg-lift-lams`` and
117117
syntactic changes:
118118

119119
#. It eliminates a let binding.
120-
#. It creates a new :term:`Top-level` binding.
120+
#. It creates a new :term:`top-level` binding.
121121
#. It replaces all occurrences of the lifted function in the let's body with a
122122
partial application. For example, all occurrences of ``f`` are replaced with
123123
``$lf b`` in the let's body.
@@ -187,9 +187,9 @@ Thus ``g``'s closure will contain ``x`` and ``f_lifted`` will be inlined, same
187187
as ``f`` in the unlifted version. ``h``'s allocations grow by one slot since
188188
``y`` *is now also* free in ``h``, just as ``x`` was. So it would seem that in
189189
total lambda lifting ``f`` saves one slot of memory because two slots were lost
190-
in ``f`` and one was gained in ``h``. However, ``g`` is a :term:`multi-shot`
191-
lambda, thus ``h`` will be allocated *for each* call of ``g``, whereas ``f`` and
192-
``g`` are only allocated once. Therefore the lift is a net loss.
190+
in ``f`` and one was gained in ``h``. However, ``g`` is a :term:`multi-shot
191+
lambda`, thus ``h`` will be allocated *for each* call of ``g``, whereas ``f``
192+
and ``g`` are only allocated once. Therefore the lift is a net loss.
193193

194194
This example illustrates how tricky good lifts can be and especially for hot
195195
loops. In general, you should try to train your eye to determine when to

src/Preliminaries/what_makes_fast_hs.rst

+4-4
Original file line numberDiff line numberDiff line change
@@ -254,10 +254,10 @@ Excessive closure allocation is subtle for two reasons: first, because GHC is
254254
typically very good at optimizing it away via :term:`Let Floating` most
255255
Haskeller's never have to confront it (which is a good indication of GHC's
256256
quality); second, in order to observe it, the programmer must track the memory
257-
allocation of their program across many functions or even modules, which is not
258-
a common experience when writing Haskell. For our purposes', we'll inspect
259-
examples that GHC should have no problem finding and optimizing. See
260-
:ref:`Case_Study_SBV`
257+
allocation of their program across many functions, modules and packages, which
258+
is not a common experience when writing Haskell. For our purposes', we'll
259+
inspect examples that GHC should have no problem finding and optimizing. See the
260+
:ref:`Impact of seq Removal on SBV's cache <SBV572>` case study for more.
261261

262262
.. todo::
263263
Not yet written, see `#18 <https://github.com/input-output-hk/hs-opt-handbook.github.io/issues/18>`_

src/glossary.rst

+55-5
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,20 @@ Glossary
1414

1515
A Boxed value is a value that is represented by a pointer to the heap.
1616

17+
Cardinality Analysis
18+
19+
A static analysis that GHC performs to determine:
20+
#. How many times a lambda-expression is called.
21+
#. Which components of a data structure are never evaluated.
22+
#. How many times a particular thunk is evaluated.
23+
24+
See :cite:t:`callArityVsDemandAnalysis` and :cite:t:`hoCardinality` for
25+
more.
26+
1727
Closure
1828

19-
A closure is value that associates a function with an environment, where
20-
the environment maps every free variable in the function with a value or
29+
A closure is value that pairs a function with an environment, where the
30+
environment maps every free variable in the function with a value or
2131
reference to which the free variable was bound when the closure was
2232
created. Closure's are the canonical way to realize lexical scoping in
2333
languages with first-class functions, such a Haskell. See `the wikipedia
@@ -199,6 +209,30 @@ Glossary
199209
type is a set with three values: ``True``, ``False``, and :math:`\bot`.
200210
Therefore ``Bool`` is a Lifted type.
201211

212+
Multi-Shot Lambda
213+
214+
A multi-shot lambda is a lambda that is called *more* than once. In
215+
contrast to a :term:`one-shot lambda`, a multi-shot lambda has a high risk
216+
of destroying :term:`sharing` if subject to certain optimizations, such as
217+
Inlining. GHC determines whether a lambda is one-shot or multi-shot during
218+
:term:`Cardinality Analysis`. See :cite:t:`hoCardinality` and
219+
:cite:t:`callArityVsDemandAnalysis` for more.
220+
221+
One-Shot Lambda
222+
223+
A one-shot lambda is a lambda that is called *exactly* once. These
224+
lambda's are common in functional programming and can be subject to more
225+
aggressive optimizations due to their one-shot nature. For example, there
226+
is no risk of losing :term:`sharing` in a one-shot lambda as a result of
227+
inlining free variables or floating let expressions *into* the lambda;
228+
something that GHC usually avoids. See :cite:t:`hoCardinality` and
229+
:cite:t:`callArityVsDemandAnalysis` for more background. See the magic
230+
`oneShot
231+
<https://hackage.haskell.org/package/base-4.17.0.0/docs/GHC-Exts.html#v:oneShot>`_
232+
function in `GHC.Exts
233+
<https://hackage.haskell.org/package/base-4.17.0.0/docs/GHC-Exts.html>`_
234+
for an unsafe way to instruct GHC that you have a one-shot lambda.
235+
202236
PAP
203237

204238
A PAP is a partial application. PAPs are heap objects and thus a type of
@@ -219,17 +253,33 @@ Glossary
219253
<https://gitlab.haskell.org/ghc/ghc/-/wikis/commentary/rts/storage/gc/pinned>`_
220254
for more.
221255

256+
Sharing
257+
258+
Consider the following program:
259+
260+
.. code-block:: haskell
261+
262+
foo :: Int -> Int
263+
foo n = let x = [1..n]
264+
in zip (fmap (* (last x)) x) x
265+
266+
We say that ``x`` is *shared* in this program because each of the three
267+
references of ``x`` refer to the ``x`` defined in the ``let``. If ``x`` is
268+
not shared that the list ``[1..n]`` would be allocated *for each*
269+
reference of ``x``. Thus, sharing is fundamental to performance oriented
270+
Haskell because it reduces allocations, leverages call-by-need, and saves
271+
work.
272+
222273
Thunk
223274

224275
A thunk is a special kind of :term:`Closure` that represents a suspended
225276
computation. Thunks reside on the heap and are the key feature that
226277
provides Haskell's laziness. See :cite:t:`SpinelessTaglessGMachine`
227278
Section 3.1.2 for more details.
228279

229-
Top-level binding
280+
Top-Level
230281

231-
A top level binding is any binding that exists in the most outer-most or
232-
global scope of the program.
282+
The most outer-most or global scope of the program.
233283

234284
Unboxed : Levity
235285

0 commit comments

Comments
 (0)