-
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
base: main
Are you sure you want to change the base?
Completely Refactor #179
Conversation
Yes, that fixed the issue with the return value ! Thanks |
@mhauru @sunxd3 apologies in advance for the rather large PR. When reviewing, I suggest that you ignore everything in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't read the last 700 lines of src/copyable_task.jl
, nor did read through the deleted tests to see that the new ones cover the same cases. I may come to those later, but figured leaving some comments now would be helpful.
I only have some localised comments and questions, and a couple of design questions. Mostly looks great, thanks so much for doing this!
end | ||
return nothing | ||
end | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test cases made me wonder, what happens if I do
f = Libtask.produce
f(0)
or something like
using Libtask: produce as prod
prod(0)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good questions. I'll add tests for this (I think it should be fine).
# Test case 1: stack allocated objects are deep copied. | ||
@testset "stack allocated objects shallow copy" begin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment and the name seem conflicting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. This test is just copied over from the old test suite, so I'm not sure what the intention was.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @KDr2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, the accurate description may be "copied by value". Back to the C-implemented version, the behavior is:
- For primitive number types, the value is copied (not deep and not shallow)
- For reference to an object, the reference is copied (not deep and not shallow, just copy the reference by the pointer value)
- Mutable objects allocated to the stack are shallow copied.
But now in the Pure-Julia version, I think only the first item holds. Maybe we should change these lines to copied by value
?
t[1] = 0 | ||
while true | ||
produce(t[1]) | ||
t[1] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this line do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe that it does anything -- I'll remove. This is another test that I've copied straight over from the old test suite.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's about an issue of the C-implemented version (maybe data initialization and stack size related). We can safely remove them(including the next similar test case) now.
@test consume(a) == 4 | ||
end | ||
end | ||
@testset "Issue: PR-86 (DynamicPPL.jl/pull/261)" begin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Am I missing something, or is this just the same test as above but with more reps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly -- I've just left this alone because it appears to be in relationship to a specific issue.
end | ||
|
||
# Test case 2: Array objects are deeply copied. | ||
@testset "Array objects deep copy" begin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it make sense to have a test like this but where the array returned by consume(ttask/a)
is mutated by the caller?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you elaborate a little bit?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering about something like
function f()
t = [0 1 2]
while true
produce(t)
t[1] = 1 + t[1]
end
end
ttask = TapedTask(nothing, f)
t = consume(ttask)
@test t[1] == 0
consume(ttask)
@test t[1] == 1
t[1] = -2
consume(ttask)
@test t[1] == -1
Or maybe that's not how it works if a copy is made, but then there could be a test to ensure that indeed a copy is made and the above doesn't work.
* Update benchmark.jl * Update benchmark.jl
Co-authored-by: Markus Hauru <[email protected]>
# ID(%2) = deref_phi(refs, ID(%1)) | ||
# set_ref_at!(refs, ref_ind_for_ID(%1), ID(%2)) | ||
# | ||
# where `deref_phi` retrives the value in position `ref_ind_for_ID(%n)` if |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# where `deref_phi` retrives the value in position `ref_ind_for_ID(%n)` if | |
# where `deref_phi` retrieves the value in position `ref_ind_for_ID(%n)` if |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is marvelous effort.
I read through src/copyable_task.jl
and I think I understand the logic, but I haven't gone through the tests.
I don't have much comment on coding style and fine-grain logics. But I want to mention some points that would be great if they can be included in the documentation:
- I think
get_taped_globals
usingtask_local_storage
is a great idea, but maybe explain a bit how and why to usetask_local_storage
? (I recall this is to support some necessary state for SMC like RNG?) - It tooks me a bit of effort to understand how
refs
is used to enable isolation between execution of copiedTapedTask
, some high level description would help a lot (maybe this already exists and I just missed it)
# in the `Ref`s that they are associated to. | ||
callable_inst_pairs = IDInstPair[] | ||
for (n, arg) in enumerate(callable_args) | ||
arg isa ID || continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume the alternative values arg
can take is something like Const
?
id = bb.inst_ids[split.last] | ||
inst = bb.insts[split.last] | ||
stmt = inst.stmt | ||
if n == length(splits) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I seems to me that the block following this assumes that the split is successful and might_produce
is placed at the end of the basic block. Is there a simple way to assert this assumption?
key::Any | ||
end | ||
|
||
const mc_cache = Dict{CacheKey,MistyClosure}() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this be dangerous in multi-thread settings?
Per #177 this is an attempt to significantly refactor the internals for robustness.
Todo:
task
field of aTapedTask
ought actually to be part of the public interface -- issue to discussion open at Accessingtask
field of Libtask TapedTask AdvancedPS.jl#113PhiNode
s are handled more thoroughly (include a worked example)optimise nested calls (currently throwing away all return type information to get prototype running)see belowLinked Issues:
task
field of Libtask TapedTask AdvancedPS.jl#113Closes #165
Closes #167
Closes #171
Closes #176
edit: performance optimisation. Everything (as far as I know) is now type-stable where it ought to be, except in the context of nested calls. These are somewhat harder to achieve optimal performance with (you need the IR of the thing that you're going to recurse into in order to figure out the return type of a call-which-might-produce -- since nasty things like recursion exist, it's not completely trivial to do this correctly).