-
Notifications
You must be signed in to change notification settings - Fork 10
Completely Refactor #179
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Completely Refactor #179
Changes from 6 commits
55dcca5
8f9c3ce
9f0ad12
ffd86e2
2d06346
0c545bb
dc5ab50
5228dc2
fc01732
5ddfb60
159fe13
61594e4
ae6d000
dc4a0be
7e9ba1e
8e6f1b6
71e6444
4d18d47
f1b247d
85ec6a2
04544ca
ecbf41c
22f6f67
04e4dcd
b25d2f4
94aed2f
1579bff
5114d64
b803f58
6aeaac3
b546f21
8f4e4b5
4d0b423
bde142f
4e58e11
21816ee
3205273
06c168f
258a4cd
a3c2162
4bb0dfa
a63660e
4e1e5df
54c745c
0102653
e751db3
6a221c2
f94ea17
57b808c
bf1893c
2c82159
b393c54
906cb66
4f80127
065fb19
1ba2dfe
999f5bc
ec9edb7
5135c4a
b42227e
aa86594
5b07ec1
bc480c2
35e9b7a
42096b3
c54dc5c
b2e65b8
01a0b34
ebe8f91
2907f03
1198038
c4237db
08cd796
052ec0a
4f48fcb
2086748
89b0e90
ec1d89d
8e7f784
0962608
1c8f77d
6a37027
b6418db
d3b5639
172e463
adff799
b081a3f
1ff18bc
8226f2f
da3a333
805cb52
21051e0
53304ee
c28c46e
4472ede
3180c15
d29a722
31e1bd5
0ef069d
001737e
6e5d6b4
dec3f43
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -222,13 +222,50 @@ julia> consume(t) | |||||
|
||||||
# Implementation Notes | ||||||
|
||||||
Under the hood, a `TapedTask` is implemented in terms of a `MistyClosure.` Each SSA value in | ||||||
the original IR is replaced with a `Base.RefValue` of the same type. Each statement writes | ||||||
to / reads from these SSAs. These refs are stored in the data that the `MistyClosure` closes | ||||||
over, meaning that they persist between calls to `consume`. These refs are copied when a | ||||||
copy of a `TapedTask` is made -- since they define the entire state of a task, making a | ||||||
completely independent copy of them ensures that a completely independent copy of the task | ||||||
is made. | ||||||
Under the hood, we implement a `TapedTask` by obtaining the `IRCode` associated to the | ||||||
original function, transforming it so that it implements the semantics required by the | ||||||
`produce` / `consume` interface, and placing it inside a `MistyClosure` to make it possible | ||||||
to execute. | ||||||
|
||||||
There are two main considerations when transforming the `IRCode`. The first is to ensure | ||||||
that the "state" of a `TapedTask` can be copied, so that a `TapedTask` can be copied, and | ||||||
resumed later. The complete state of a `TapedTask` is given by its arguments, and the value | ||||||
associated to each ssa (these are initially undefined). | ||||||
To make it possible to copy the state of the ssa values, we place a `Base.RefValue{T}`s into | ||||||
the captures of the `MistyClosure` which implements the `TapedTask`, one for each ssa in the | ||||||
IR (`T` is the type inferred for that ssa). A call is replaced by reading in values of ssas | ||||||
from these refs, applying the original operation, and writing the result to the ref | ||||||
associated to the instruction. For example, if the original snippet of `IRCode` is something | ||||||
like | ||||||
```julia | ||||||
%5 = f(%3, _1) | ||||||
``` | ||||||
the transformed IR would be something like | ||||||
```julia | ||||||
%5 = ref_for_%3[] | ||||||
%6 = f(%5, _1) | ||||||
ref_for_%5[] = %6 | ||||||
willtebbutt marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
``` | ||||||
Setting things up in this manner ensures that an independent copy is made by simply copying | ||||||
all of the refs. A `deepcopy` is required for correctness as, while the refs to not alias | ||||||
willtebbutt marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
one another (by construction), their contents might. For example, two refs may contain the | ||||||
same `Array`, and in general the behaviour of a function depends on this relationship. | ||||||
|
||||||
The second component of the transformation is implementing the `produce` mechanism, and the | ||||||
ability to resume computation from where we produced. Roughly speaking, the `IRCode` must be | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A hint on how resumption is implemented helps demystify what happens under the hood. I'm not sure what I wrote is accurate, so feel free to improve it.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, right, yes. It's similar, but not quite the same mechanism as the |
||||||
modified to ensure that whenever a `produce` call in encountered, the `MistyClosure` returns | ||||||
the argument to `produce`, and that subsequent calls resume computation immediately after | ||||||
the `produce` statement. This resumption is achieved by setting the value of a counter | ||||||
prior to returning following a `produce` statement -- a sequence of comparisons against this | ||||||
counter, and [`GotoIfNot`](https://docs.julialang.org/en/v1/devdocs/ast/#Lowered-form) | ||||||
statement are inserted at the top of the IR. These are used to jump to the point in the code | ||||||
from which computation should resume. These are set up such that, when the `TapedTask` is | ||||||
first run, computation start froms the first statement. Observe that this is also | ||||||
facilitated by the ref mechanism discussed above, as it ensures that the state persists | ||||||
between calls to a `MistyClosure`. | ||||||
|
||||||
The above gives the broad outline of how `TapedTask`s are implemented. We refer interested | ||||||
readers to the code, which is extensively commented to explain implementation details. | ||||||
""" | ||||||
function TapedTask(taped_globals::Any, fargs...; kwargs...) | ||||||
all_args = isempty(kwargs) ? fargs : (Core.kwcall, getfield(kwargs, :data), fargs...) | ||||||
|
@@ -237,6 +274,22 @@ function TapedTask(taped_globals::Any, fargs...; kwargs...) | |||||
return TapedTask(taped_globals, all_args, mc, count_ref) | ||||||
end | ||||||
|
||||||
""" | ||||||
fresh_copy(mc::T) where {T<:MistyClosure} | ||||||
|
||||||
Creates an independent copy of `mc` by (carefully) replacing the `Ref`s it | ||||||
contains it its `captures`. The resuting `MistyClosure` is safe to run. | ||||||
|
||||||
This is achieved by replacing most `Ref`s with new `Ref`s of the same (el)type, | ||||||
but with nothing stored in them -- values will be stored in them when the | ||||||
new `MistyClosure` is called. The only exception are `DynamicCallable`s and | ||||||
`LazyCallable`s -- these are constructed when the `MistyClosure`s IR is derived, | ||||||
so fresh instances of them are placed in the associated `Ref`. | ||||||
|
||||||
The position counter is reset to `-1` (indicating that execution should proceed | ||||||
from the start of the IR, rather than eg. jumping to a line following a | ||||||
`produce` statement. | ||||||
""" | ||||||
function fresh_copy(mc::T) where {T<:MistyClosure} | ||||||
willtebbutt marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
new_captures = map(mc.oc.captures) do r | ||||||
if eltype(r) <: DynamicCallable | ||||||
|
@@ -407,6 +460,15 @@ const TypeInfo = Tuple{Vector{Any},Dict{ID,Type}} | |||||
_typeof(x) | ||||||
|
||||||
Central definition of typeof, which is specific to the use-required in this package. | ||||||
Largely the same as `Base._stable_typeof`, differing only in a handful of | ||||||
situations, for example: | ||||||
```jldoctest | ||||||
julia> Base._stable_typeof((Float64,)) | ||||||
Tuple{DataType} | ||||||
|
||||||
julia> Libtask._typeof((Float64,)) | ||||||
Tuple{Type{Float64}} | ||||||
``` | ||||||
""" | ||||||
_typeof(x) = Base._stable_typeof(x) | ||||||
_typeof(x::Tuple) = Tuple{map(_typeof, x)...} | ||||||
|
@@ -865,6 +927,11 @@ function derive_copyable_task_ir(ir::BBCode)::Tuple{BBCode,Tuple,Vector{Any}} | |||||
# At present, we're not able to properly infer the values which might | ||||||
# potentially be produced by a call-which-might-produce. Consequently, we | ||||||
# have to assume they can produce anything. | ||||||
# | ||||||
# This `Any` only affects the return type of the function being derived | ||||||
# here. Importantly, it does not affect the type stability of subsequent | ||||||
# statements in this function. As a result, the impact ought to be | ||||||
# reasoanbly limited. | ||||||
willtebbutt marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
push!(possible_produce_types, Any) | ||||||
willtebbutt marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
# Create a new basic block from the existing statements, since all new | ||||||
|
Uh oh!
There was an error while loading. Please reload this page.