forked from JuliaLang/julia
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Change terminology according to JuliaLang#10884 [av skip]
- Loading branch information
Tomas Lycken
committed
Apr 20, 2015
1 parent
45db851
commit c4719d2
Showing
1 changed file
with
61 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -872,49 +872,52 @@ entirely in Julia. You can read their source and see precisely what they | |
do — and all they do is construct expression objects to be inserted into | ||
your program's syntax tree. | ||
|
||
Staged functions | ||
Generated functions | ||
---------------- | ||
This comment has been minimized.
Sorry, something went wrong. |
||
|
||
*Staged functions* play a similar role as macros, but at a later stage | ||
between parsing and run-time. Staged functions give the capability to | ||
generate specialized code depending on the types of their arguments. | ||
While macros work with expressions at parsing-time and cannot access the | ||
types of their inputs, a staged function gets expanded at a time when | ||
the types of the arguments are known, but the function is not yet compiled. | ||
A very special macro is ``@generated``, which allows you to define so-called | ||
*generated functions*. These have the capability to generate specialized | ||
code depending on the types of their arguments with more flexibility and/or | ||
less code than what can be achieved with multiple dispatch. While macros | ||
This comment has been minimized.
Sorry, something went wrong.
mbauman
|
||
work with expressions at parsing-time and cannot access the types of their | ||
inputs, a generated function gets expanded at a time when the types of | ||
the arguments are known, but the function is not yet compiled. | ||
|
||
Depending on the types of the arguments, a staged function returns a quoted | ||
expression which then forms the function body of the specialized function. | ||
Thus, staged functions provide a flexible framework to move work from | ||
run-time to compile-time. | ||
Depending on the types of the arguments, a generated function returns a | ||
quoted expression which then forms the method body of the specialized | ||
method. Thus, generated functions provide a flexible framework to move | ||
work from run-time to compile-time. | ||
|
||
When defining staged functions, there are three main differences to | ||
When defining generated functions, there are three main differences to | ||
ordinary functions: | ||
|
||
1. You use the keyword ``stagedfunction`` instead of ``function`` | ||
1. You annotate the function declaration with the ``@generated`` macro. | ||
This adds some information to the AST that lets the compiler know that | ||
this is a generated function. | ||
|
||
2. In the body of the ``stagedfunction`` you only have access to the | ||
2. In the body of the generated function you only have access to the | ||
*types* of the arguments, not their values. | ||
|
||
3. Instead of calculating something or performing some action, you return | ||
from the staged function a *quoted expression* which, when evaluated, | ||
does what you want. | ||
from a *quoted expression* which, when evaluated, does what you want. | ||
|
||
It's easiest to illustrate this with an example. We can declare a staged | ||
It's easiest to illustrate this with an example. We can declare a generated | ||
function ``foo`` as | ||
|
||
.. doctest:: | ||
|
||
julia> stagedfunction foo(x) | ||
julia> @generate foo(x) | ||
This comment has been minimized.
Sorry, something went wrong. |
||
println(x) | ||
return :(x*x) | ||
end | ||
foo (generic function with 1 method) | ||
|
||
Note that the body returns a quoted expression, namely ``x*x``. | ||
Note that the body returns a quoted expression, namely ``:(x*x)``, rather | ||
than just the value of ``x*x``. | ||
|
||
From the callers perspective, they are very similar to regular functions; | ||
in fact, you don't have to know if you're calling a ``function`` or a | ||
``stagedfunction`` - the syntax and result of the call is just the same. | ||
in fact, you don't have to know if you're calling a regular or generated | ||
function or a - the syntax and result of the call is just the same. | ||
Let's see how ``foo`` behaves: | ||
|
||
.. doctest:: | ||
|
@@ -929,10 +932,10 @@ Let's see how ``foo`` behaves: | |
julia> y | ||
"barbar" | ||
|
||
So, we see that in the body of the ``stagedfunction``, ``x`` is the | ||
*type* of the passed argument, and the value returned by the ``stagedfunction``, | ||
is the result of evaluating the quoted expression we returned from the | ||
definition, now with the *value* of ``x``. | ||
So, we see that in the body of the generated function, ``x`` is the | ||
*type* of the passed argument, and the value returned by the generated | ||
function, is the result of evaluating the quoted expression we returned | ||
from the definition, now with the *value* of ``x``. | ||
|
||
What happens if we evaluate ``foo`` again with a type that we have already | ||
used? | ||
|
@@ -942,29 +945,29 @@ used? | |
julia> foo(4) | ||
16 | ||
|
||
Note that there is no printout of ``Int64``. The body of the ``stagedfunction`` | ||
is only executed *once* (not entirely true, see note below) when the method | ||
for that specific set of argument types is compiled. After that, the | ||
expression returned from the ``stagedfunction`` on the first invocation | ||
Note that there is no printout of ``Int64``. The body of the generated | ||
function is only executed *once* (not entirely true, see note below) when | ||
the method for that specific set of argument types is compiled. After that, | ||
the expression returned from the generated function on the first invocation | ||
is re-used as the method body. | ||
|
||
The reason for the disclaimer above is that the number of times a staged | ||
function is staged is really an implementation detail; it *might* be only | ||
The reason for the disclaimer above is that the number of times a generated | ||
function is generated is really an implementation detail; it *might* be only | ||
once, but it *might* also be more often. As a consequence, you should | ||
*never* write a staged function with side effects - when, and how often, | ||
*never* write a generated function with side effects - when, and how often, | ||
the side effects occur is undefined. (This is true for macros too - and just | ||
like for macros, the use of `eval` in a staged function is a sign that | ||
like for macros, the use of `eval` in a generated function is a sign that | ||
you're doing something the wrong way.) | ||
|
||
The example staged function ``foo`` above did not do anything a normal | ||
The example generated function ``foo`` above did not do anything a normal | ||
function ``foo(x)=x*x`` could not do, except printing the the type on the | ||
first invocation and incurring a higher compile-time cost. However, the | ||
power of a staged function lies in its ability to compute different quoted | ||
first invocation (and incurring a higher compile-time cost). However, the | ||
power of a generated function lies in its ability to compute different quoted | ||
expression depending on the types passed to it: | ||
|
||
.. doctest:: | ||
|
||
julia> stagedfunction bar(x) | ||
julia> @generated function bar(x) | ||
if x <: Integer | ||
return :(x^2) | ||
else | ||
|
@@ -978,27 +981,30 @@ expression depending on the types passed to it: | |
julia> bar("baz") | ||
"baz" | ||
|
||
(although of course this contrived example is easily implemented using | ||
multiple dispatch...) | ||
|
||
We can, of course, abuse this to produce some interesting behavior:: | ||
|
||
julia> stagedfunction baz(x) | ||
julia> @generated function baz(x) | ||
if rand() < .9 | ||
return :(x^2) | ||
else | ||
return :("boo!") | ||
end | ||
end | ||
|
||
Since the body of the staged function is non-deterministic, its behavior | ||
Since the body of the generated function is non-deterministic, its behavior | ||
is undefined; the expression returned on the *first* invocation will be | ||
used for *all* subsequent invocations with the same type (again, with the | ||
exception covered by the disclaimer above). When we call the staged | ||
exception covered by the disclaimer above). When we call the generated | ||
function with ``x`` of a new type, ``rand()`` will be called again to | ||
see which method body to use for the new type. In this case, for one | ||
*type* out of ten, ``baz(x)`` will return the string ``"boo!"``. | ||
|
||
*Don't copy these examples!* | ||
|
||
These examples are hopefully helpful to illustrate how staged functions | ||
These examples are hopefully helpful to illustrate how generated functions | ||
work, both in the definition end and at the call site; however, *don't | ||
copy them*, for the following reasons: | ||
|
||
|
@@ -1009,7 +1015,7 @@ copy them*, for the following reasons: | |
the same thing, but it is both simpler and faster. | ||
* the `baz` function is pathologically insane | ||
|
||
Instead, now that we have a better understanding for how staged functions | ||
Instead, now that we have a better understanding for how generated functions | ||
work, let's use them to build some more advanced functionality... | ||
|
||
An advanced example | ||
|
@@ -1043,46 +1049,46 @@ thing: a runtime loop over the dimensions of the array, collecting the | |
offset in each dimension into the final index. | ||
|
||
However, all the information we need for the loop is embedded in the type | ||
information of the arguments. Thus, we can utilize staged functions to | ||
move the iteration to compile-time; in compiler parlance, we use staged | ||
information of the arguments. Thus, we can utilize generated functions to | ||
move the iteration to compile-time; in compiler parlance, we use generated | ||
functions to manually unroll the loop. The body becomes almost identical, | ||
but instead of calculating the linear index, we build up an *expression* | ||
that calculates the index:: | ||
|
||
stagedfunction sub2ind_staged{N}(dims::NTuple{N}, I::Integer...) | ||
@generated function sub2ind_gen{N}(dims::NTuple{N}, I::Integer...) | ||
ex = :(I[$N] - 1) | ||
for i = N-1:-1:1 | ||
ex = :(I[$i] - 1 + dims[$i]*$ex) | ||
end | ||
return :($ex + 1) | ||
end | ||
|
||
**What code will this staged function generate?** | ||
**What code will this generate?** | ||
|
||
An easy way to find out, is to extract the body into another (regular) | ||
function:: | ||
|
||
stagedfunction sub2ind_staged{N}(dims::NTuple{N}, I::Integer...) | ||
sub2ind_staged_impl(dims, I...) | ||
@generated function sub2ind_gen{N}(dims::NTuple{N}, I::Integer...) | ||
sub2ind_gen_impl(dims, I...) | ||
end | ||
|
||
function sub2ind_staged_impl{N}(dims::NTuple{N}, I...) | ||
function sub2ind_gen_impl{N}(dims::NTuple{N}, I...) | ||
ex = :(I[$N] - 1) | ||
for i = N-1:-1:1 | ||
ex = :(I[$i] - 1 + dims[$i]*$ex) | ||
end | ||
return :($ex + 1) | ||
end | ||
|
||
We can now execute ``sub2ind_staged_impl`` and examine the expression it | ||
We can now execute ``sub2ind_gen_impl`` and examine the expression it | ||
returns:: | ||
|
||
julia> sub2ind_staged_impl((Int,Int), Int, Int) | ||
julia> sub2ind_gen_impl((Int,Int), Int, Int) | ||
:(((I[1] - 1) + dims[1] * ex) + 1) | ||
|
||
So, the method body that will be used here doesn't include a loop at all | ||
- just indexing into the two tuples, multiplication and addition/subtraction. | ||
All the looping is performed compile-time, and we avoid looping during execution | ||
entirely. Thus, we only loop *once per type*, in this case once per ``N`` | ||
(except in edge cases where the function is staged more than once - see | ||
disclaimer above). | ||
All the looping is performed compile-time, and we avoid looping during | ||
execution entirely. Thus, we only loop *once per type*, in this case once | ||
per ``N`` (except in edge cases where the function is generated more than | ||
once - see disclaimer above). |
I don't know RST very well, but I do know it's pretty picky at times. Does it need this underline to be the same length as the header?