Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
55dcca5
Rework test cases a bit
willtebbutt Feb 27, 2025
8f9c3ce
Add formatter config
willtebbutt Feb 27, 2025
9f0ad12
Update gitignore to remove redundant items
willtebbutt Feb 27, 2025
ffd86e2
Bump minor version because small breaking change
willtebbutt Feb 27, 2025
2d06346
Tell users that various old types have been actually removed
willtebbutt Feb 27, 2025
0c545bb
Formatting of perf
willtebbutt Feb 27, 2025
dc5ab50
Remove old code
willtebbutt Feb 27, 2025
5228dc2
Rework includes in package and runtests
willtebbutt Feb 27, 2025
fc01732
More formatting
willtebbutt Feb 27, 2025
5ddfb60
Add in copyable_task files
willtebbutt Feb 27, 2025
159fe13
Clean up runtests
willtebbutt Feb 27, 2025
61594e4
Update project deps etc
willtebbutt Feb 27, 2025
ae6d000
Drop 1.7 from CI
willtebbutt Feb 27, 2025
dc4a0be
Initial transfer of code
willtebbutt Feb 27, 2025
7e9ba1e
Fix bug for function with no produce statements
willtebbutt Feb 27, 2025
8e6f1b6
Test for construction of new mutable struct
willtebbutt Feb 27, 2025
71e6444
Fix more test cases
willtebbutt Feb 27, 2025
4d18d47
Add another test case
willtebbutt Feb 27, 2025
f1b247d
More test cases
willtebbutt Feb 27, 2025
85ec6a2
More work
willtebbutt Feb 27, 2025
04544ca
Formatting
willtebbutt Feb 27, 2025
ecbf41c
Remove unhelpful docstring
willtebbutt Feb 27, 2025
22f6f67
Relax Test compat a bit
willtebbutt Feb 27, 2025
04e4dcd
Lower minor version to make integration tests run
willtebbutt Feb 27, 2025
b25d2f4
Handle code_coverage_effect
willtebbutt Feb 27, 2025
94aed2f
Some tweaks + docs
willtebbutt Feb 28, 2025
1579bff
Fix copying
willtebbutt Feb 28, 2025
5114d64
Formatting
willtebbutt Feb 28, 2025
b803f58
Enable more tests
willtebbutt Feb 28, 2025
6aeaac3
Implement dynamic scope
willtebbutt Mar 7, 2025
b546f21
Test dynamic scope correctness
willtebbutt Mar 7, 2025
8f4e4b5
README and NEWS overhaul
willtebbutt Mar 7, 2025
4d0b423
Export get_dynamic_scope and set_dynamic_scope
willtebbutt Mar 7, 2025
bde142f
Placeholder docstring for produce
willtebbutt Mar 7, 2025
4e58e11
Initial docs
willtebbutt Mar 7, 2025
21816ee
Ignore build folder of docs
willtebbutt Mar 7, 2025
3205273
Update cache action
willtebbutt Mar 7, 2025
06c168f
Formatting
willtebbutt Mar 7, 2025
258a4cd
Ignore all top-level manifest files
willtebbutt Mar 7, 2025
a3c2162
Add dependency on ScopedValues
willtebbutt Mar 7, 2025
4bb0dfa
Fix on LTS
willtebbutt Mar 7, 2025
a63660e
Do not check for stale deps on 1.11
willtebbutt Mar 7, 2025
4e1e5df
Some docs
willtebbutt Mar 7, 2025
54c745c
Docs action
willtebbutt Mar 7, 2025
0102653
Tidy up docs
willtebbutt Mar 7, 2025
e751db3
Tidy up docs slightly
willtebbutt Mar 7, 2025
6a221c2
Refactor + basic nested produce handling
willtebbutt Mar 11, 2025
f94ea17
Fomatting
willtebbutt Mar 11, 2025
57b808c
Dynamic nested calls and uses of return values of calls which might p…
willtebbutt Mar 13, 2025
bf1893c
Fix docs build
willtebbutt Mar 13, 2025
2c82159
Update CI
willtebbutt Mar 13, 2025
b393c54
Handle callable structs
willtebbutt Mar 13, 2025
906cb66
Fix docs and add doctest
willtebbutt Mar 20, 2025
4f80127
Test kwargs
willtebbutt Apr 8, 2025
065fb19
More tests
willtebbutt Apr 8, 2025
1ba2dfe
Fix inference bug
willtebbutt Apr 8, 2025
999f5bc
Improve documentation
willtebbutt Apr 8, 2025
ec9edb7
Performance enhancements
willtebbutt Apr 15, 2025
5135c4a
Caching and tweaks
willtebbutt Apr 15, 2025
b42227e
Fix docs build
willtebbutt Apr 15, 2025
aa86594
Docs and tidy up
willtebbutt Apr 15, 2025
5b07ec1
Include specifics from Mooncake
willtebbutt Apr 15, 2025
bc480c2
Tidy up further
willtebbutt Apr 15, 2025
35e9b7a
Tidy up further
willtebbutt Apr 15, 2025
42096b3
Merge branch 'main' into wct/refactor
willtebbutt Apr 15, 2025
c54dc5c
Remove undefined export
willtebbutt Apr 15, 2025
b2e65b8
Bump patch version
willtebbutt Apr 15, 2025
01a0b34
Test produce global performance
willtebbutt Apr 15, 2025
ebe8f91
Remove more references to dynamic scope
willtebbutt Apr 15, 2025
2907f03
Document type assertion
willtebbutt Apr 15, 2025
1198038
Fix benchmarks (#180)
yebai Apr 22, 2025
c4237db
Apply suggestions from code review
willtebbutt Apr 22, 2025
08cd796
Fix loopinfo expression
willtebbutt Apr 22, 2025
052ec0a
Remove redundant extended help section
willtebbutt Apr 22, 2025
4f48fcb
Note why __v exists
willtebbutt Apr 22, 2025
2086748
Sort out consume
willtebbutt Apr 22, 2025
89b0e90
Explain produce function
willtebbutt Apr 22, 2025
ec1d89d
Document callable_ret_type
willtebbutt Apr 22, 2025
8e7f784
Update benchmark.jl
yebai Apr 22, 2025
0962608
Docstring for build_callable
willtebbutt May 5, 2025
1c8f77d
Tweaks to build_callable and docs
willtebbutt May 5, 2025
6a37027
Merge branch 'wct/refactor' of https://github.com/TuringLang/Libtask.…
willtebbutt May 5, 2025
b6418db
Document aliasing
willtebbutt May 5, 2025
d3b5639
Document bbcode usage
willtebbutt May 5, 2025
172e463
More docs
willtebbutt May 5, 2025
adff799
Formatting and remove unnecessary import
willtebbutt May 5, 2025
b081a3f
Remove redundant comment
willtebbutt May 5, 2025
1ff18bc
Fix test wording
willtebbutt May 5, 2025
8226f2f
Remove redundant test cases
willtebbutt May 5, 2025
da3a333
Fix typo
willtebbutt May 5, 2025
805cb52
Formatting
willtebbutt May 5, 2025
21051e0
Docstring
willtebbutt May 5, 2025
53304ee
Fix docs bulid
willtebbutt May 5, 2025
c28c46e
Docstring for fresh_copy
willtebbutt May 9, 2025
4472ede
More docs
willtebbutt May 9, 2025
3180c15
Expand on refs and misty closure usage
willtebbutt May 9, 2025
d29a722
Explain gotoifnot a bit more carefully
willtebbutt May 9, 2025
31e1bd5
Link to GotoIfNot in Julia docs
willtebbutt May 9, 2025
0ef069d
Update Documentation.yml (#181)
yebai May 9, 2025
001737e
Apply suggestions from code review
willtebbutt May 9, 2025
6e5d6b4
Update src/copyable_task.jl
willtebbutt May 9, 2025
dec3f43
Update src/copyable_task.jl
willtebbutt May 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 8 additions & 20 deletions .github/workflows/Documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,14 @@ on:
tags: '*'
pull_request:

permissions:
contents: write
pull-requests: write

jobs:
build:
permissions:
contents: write
pull-requests: read
statuses: write
actions: write
docs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: julia-actions/setup-julia@v2
with:
version: '1'
arch: x64
include-all-prereleases: false
- name: Install dependencies
run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.update(); Pkg.instantiate()'
- name: Build and deploy
env:
GKSwstype: nul # turn off GR's interactive plotting for notebooks
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # If authenticating with GitHub Actions token
DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # For authentication with SSH deploy key
run: julia --project=docs/ docs/make.jl
- name: Build and deploy Documenter.jl docs
uses: TuringLang/actions/DocsDocumenter@main
1 change: 1 addition & 0 deletions docs/src/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ Libtask.build_callable
Libtask.LazyCallable
Libtask.DynamicCallable
Libtask.callable_ret_type
Libtask.fresh_copy
```
81 changes: 74 additions & 7 deletions src/copyable_task.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
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
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
Copy link
Member

Choose a reason for hiding this comment

The 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
ability to resume computation from where we produced. Roughly speaking, the `IRCode` must be
ability to resume computation from where we produced (via [`goto` ](https://docs.julialang.org/en/v1/base/base/#Base.@goto)statements). Roughly speaking, the `IRCode` must be

Copy link
Member Author

Choose a reason for hiding this comment

The 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 @goto statement. I've tweaked the docstring accordingly.

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...)
Expand All @@ -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}
new_captures = map(mc.oc.captures) do r
if eltype(r) <: DynamicCallable
Expand Down Expand Up @@ -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)...}
Expand Down Expand Up @@ -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.
push!(possible_produce_types, Any)

# Create a new basic block from the existing statements, since all new
Expand Down
Loading