Skip to content
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

make at-everywhere import modules before remote calls #21718

Merged
merged 4 commits into from
May 12, 2017

Conversation

stevengj
Copy link
Member

@stevengj stevengj commented May 5, 2017

This PR is intended to make @everywhere using Foo a bit more reliable and performant, by turning it into essentially import Foo; @everywhere using Foo. Similarly for @everwhere begin ... end and other blocks that might contain import or using statements.

e.g. right now @everywhere using Foo gives WARNING: replacing module Foo. because it ends up importing Foo twice, and that in turn can cause problems with certain modules (e.g. PyCall: JuliaPy/PyCall.jl#388) that don't like to be initialized twice. Also there are potential race conditions with cache recompilation.

Not completely sure how to implement a test for this.

@kshyatt kshyatt added modules parallelism Parallel or distributed computation labels May 5, 2017
@kshyatt kshyatt requested a review from amitmurthy May 7, 2017 22:28
@amitmurthy
Copy link
Contributor

Wouldn't it be better to treat @everywhere using Foo as a import Foo; @everywhere importall Foo? Will also address JuliaLang/Distributed.jl#20

@timholy
Copy link
Member

timholy commented May 8, 2017

Having using mean the same thing as importall would mean that I couldn't "override" (vs. "extend") a method in Foo.

@@ -48,6 +48,24 @@ macro fetchfrom(p, expr)
:(remotecall_fetch($thunk, $(esc(p))))
end

# extract a list of modules to import from an expression
extract_imports(x) = Symbol[]
function extract_imports(ex::Expr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure whether it's a big deal, but this might be more efficient as

function extract_imports!(imports, ex::Expr)

and having the everywhere caller execute it as extract_imports!(Symbol[], ex). That way you don't have to allocate an array for every single token.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, will do.

@StefanKarpinski
Copy link
Member

StefanKarpinski commented May 8, 2017

+100 to this PR.

-1000 on having using silently translated to importall. Why would that be better?

@amitmurthy
Copy link
Contributor

-1000 on having using silently translated to importall. Why would that be better?

That was a stupid suggestion. I was under the mistaken assumption that using on the workers would trigger a load of the module on all other workers.

@stevengj stevengj force-pushed the everywhereusing branch from 3452589 to 305037a Compare May 8, 2017 21:07
@stevengj
Copy link
Member Author

stevengj commented May 8, 2017

I eliminated the unnecessary array allocations mentioned by @timholy, as well as unnecessary allocations introduced by isexpr(ex, [...heads...]).

For the latter, since we already supported isexpr(ex, heads) for heads::Union{Set,Vector}, I simply added Tuple and changed calls to use it. (The isexpr function is undocumented, albeit widely used in almost 100 packages.) In a microbenchmark, isexpr(ex, [...]) is about 2-3x slower than isexpr(ex, (...)).

test/meta.jl Outdated
@@ -122,13 +122,17 @@ using Base.Meta

@test isexpr(:(1+1),Set([:call]))
@test isexpr(:(1+1),Vector([:call]))
@test isexpr(:(1+1),(:call))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to pass a tuple here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whoops, right

@stevengj
Copy link
Member Author

Travis failure was a network timeout:

INFO: Cloning METADATA from https://github.com/JuliaLang/METADATA.jl
  0     0    0     0    0     0      0      0 --:--:--  0:01:15 --:--:--     0INFO: No packages to install, update or remove
  0     0    0     0    0     0      0      0 --:--:--  0:01:18 --:--:--     0curl: (7) Failed to connect to 192.0.2.1 port 80: Operation timed out
download (3)       |   80.40  |  0.00  |  0.0 |  7.94      | 179.59  

@JeffBezanson
Copy link
Member

Actually, I bet this is working around a bug. No combination of local and remote usings should try to load the same thing twice on one machine.

@amitmurthy
Copy link
Contributor

Yes, there is an open issue for the bug - #19187 .

@amitmurthy
Copy link
Contributor

This issue has been known and open for quite sometime, hence I am for merging this workaround for now - can be removed when the bug is fixed.

@JeffBezanson
Copy link
Member

Hang on, I might have a fix.

@JeffBezanson
Copy link
Member

Ok, I've been trying various things. The immediate cause of the issue is that _require_from_serialized tells worker processes to load a package without respecting package_locks. That can be fixed pretty easily, but then we hit the more fundamental issue that there's no synchronization mechanism for precompilation itself --- we don't have a way to know whether another node is precompiling a package, and if so, wait for it to finish. So I think a full fix for this requires fixing #17320. We should probably merge this PR now, and aim to fix #17320 in 0.6.1.

@JeffBezanson JeffBezanson merged commit f92387b into JuliaLang:master May 12, 2017
@stevengj stevengj deleted the everywhereusing branch May 12, 2017 14:20
@tkelman
Copy link
Contributor

tkelman commented May 14, 2017

this should have been squashed, the intermediate commits fail tests

tkelman pushed a commit that referenced this pull request May 14, 2017
… calls

(cherry picked from commit 66b8036)
ref #21718

don't insert toplevel expression unless there are imports

(cherry picked from commit 18291b0)

eliminate unnecessary array allocations from isexpr and extract_imports functions

(cherry picked from commit 305037a)

test fixes

(cherry picked from commit 24a74a5)
@pwl
Copy link
Contributor

pwl commented Jul 13, 2017

After upgrading to 0.6 I started having

julia> f()=@everywhere using Base.Test
f (generic function with 1 method)

julia> f()
ERROR: error compiling f: unsupported or misplaced expression "toplevel" in function f
Stacktrace:
 [1] macro expansion at ./REPL.jl:97 [inlined]
 [2] (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at ./event.jl:73

Is the above error intended or is it a bug?

@iblis17 suggested to use f()=@eval @everywhere using Base.Test instead, which fixed the issue.

@stevengj
Copy link
Member Author

@pwl, you can't do a using statement inside a function. It really shouldn't have worked before.

@pwl
Copy link
Contributor

pwl commented Jul 13, 2017

@stevengj you are totally right, my mistake. In any case, the error is rather cryptic, without the @everywhere I'm getting ERROR: error compiling f: unsupported or misplaced expression "using" in function f, which is more explicit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
modules parallelism Parallel or distributed computation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants