-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
x/tools/go/packages: Load is 2-3 orders of magnitude slower than go/build.Import #31087
Comments
This was discussed offline: The slowness was due to the Google-specific gopackagesdriver binary. Unfortunately go/packages will always be slower than go/build because we need to execute go list as the true authority of package information, but this kind of slowness is unexpected and really bad. We'll get a google-specific fix in soon, but for those of you facing this problem, you can set GOPACKAGESDRIVER=off in the short term to bypass that driver. |
Thanks. I am still concerned at "go/packages will always be slower than go/build" even when we have vendored EVERYTHING. I will hang tight for the followup here, and try a proper rewrite of my real tool against go/packages (rather than a trivial adaptation as I did) |
re: always slower, it's true but I doubt it'll be a big problem for Kubernetes' build tools. There's some amount of overhead involved in running After that the major issue is that |
ACK the last part. A simple adaptation of go2make would call it many many
times. I will overhaul it to optimize for that.
…On Wed, Mar 27, 2019, 4:27 PM Heschi Kreinick ***@***.***> wrote:
re: always slower, it's true but I doubt it'll be a big problem for
Kubernetes' build tools.
There's some amount of overhead involved in running go list, then
marshalling and unmarshalling the results. In my experience that's usually
small, say 50-100ms or so. That can be a problem for highly interactive
tools like goimports and godef, but for a build tool like go2make I think
it'd be okay.
After that the major issue is that packages.Load starts from scratch
every time, so it's important to keep the number of calls low to avoid
loading the same packages (e.g. the stdlib) over and over and over. That
can be really easy, or require major refactorings. But for most people I
don't think it's been a big problem.
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#31087 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AFVgVIBwf1G1Y71UpgIgDHjizLF9qf-oks5va_5NgaJpZM4cOi1N>
.
|
Note that issues like #30826 and #30828 make it harder to avoid calling |
Change https://golang.org/cl/170863 mentions this issue: |
I'm baaaaack! This bug started with me trying to convert from go/build to go/packages. Trivial conversion was not going to work, so I back-burnered it. But now even go/build is slow (not sure when it happened). I noticed that we still set The below program runs very fast with GO111MODULE=off and very slow with it on. Same binary, same inputs, 100x slower.
From before:
It looks like there's an internal switch that happened at some point and without The problem is that this tool runs on every Kubernetes build. In the legacy mode it takes 3-5 seconds for the whole repo, which is acceptable. Adding 5+ minutes to each run (even just a small incremental run) is obviously not viable. So I guess the question I have is - why is this OK? Am I just so far outside the norm that 100x slowdown is acceptable in exchange for some (presumably) internal cleanup? Can I rely on Is there a better answer? Program:
|
@thockin Just from a quick look, it seems like the
I don't quite understand what the program intends to accomplish, other than doing the loading and printing debug logs. I assume it's just for the sake of illustrating the slowness. From a tooling point of view, the strategy above is really slow. To support modules, both
I think it would be more helpful if you explained what exactly your program is trying to do. I'm pretty convinced you could load all the packages you need in one single underlying |
Also, I'm happy to take this somewhere else if you would like to discuss your program in more detail. GitHub is not a very good place to provide help of this kind :) |
Precisely. The real program looks at all loaded packages and emits some Makefile snippets for them, but that did not seem relevant to demonstrating the problem.
It was not slow when it was written. It's not slow with GO111MODULE=off. Modules totally borked it.
This is for Kubernetes. I have a really slow codegen tool that I want to run ONLY if the packages it consumes have ACTUALLY changed (mtime is a good enough proxy). To make it harder, I don't know a priori which packages need codegen. I want to scan the file tree, find the files that need codegen, check their deps, and only then run the codegen (actually we have several codegens in the same mold). A single
I started writing a "smarter" tool to only call I have a thread going on golang-nuts on this, and @jayconrod opened #43733 as part of that. I admit that I am not 100% sure the fix there would fix me, but it seems like it would. There's terminology and philosophy around modules that I don't quite understand, so it's hard for me to say what is expected and what is a bug. What I know is that I keep running into dead-ends. Things I would expect to work, don't. |
When I say this is slow, I mean in the context of modules. Some tools that got away with being fast in the GOPATH world will need to be re-thought in the world of modules to remain fast, because modules are more powerful but also more complex. I know that can be frustrating, which is why I'm trying to help.
Right, I was not aware the single repository contained many modules. It seems like you could use one
If you can link to that work-in-progress software, as well as the existing program that currently works, I can take a look and try to come up with a working version that's fast enough with modules. Your summary above is helpful, but ultimately I can't properly help until I see the code. |
I tried that - it takes forever. There are a lot of cross-references and some very large modules end up being processed repeatedly. What used to take 2-4 seconds now takes 45. :(
I appreciate the effort! Existing: https://github.com/kubernetes/kubernetes/tree/master/hack/make-rules/helpers/go2make New one is very cobbled together right now as I was trying to find some way to make it produce correct results without resorting to GO111MODULE=off. Will paste a file here. Run as
|
Thanks! While I look at that, I thought I'd point you towards Go's build cache. It already keeps track of "cache IDs" or keys for each package, which change if its source code or any transitive dependencies change. That might be a faster and possibly simpler approach to accomplish this goal, and it also mimics exactly what
The basic idea is: if that path/string changes, it's a different build. The command will build the package(s) in question, but that shoudl be fast if they are already in the build cache. Depends on whether you want to leave the heavy lifting to |
I just realised you can obtain all the information you need from just the root module, because it includes all the other k8s.io modules in the vendor folder. I think that should result in a working program that takes a few seconds as well. I'll share some code later today. |
Alright, here you go: mvdan/kubernetes@736c51e See the commit message for details. Runs well for me in module mode, and even a bit faster than the old program in GOPATH mode. If you wonder why use |
First, thanks for the time!
I think this would be an ideal state if I can get there - I'd rather NOT reinvent parts of go. That said, it will require some retooling of the rest of the build, which assumes a bunch of directory-specific variables. If that hits a dead-end, I'll circle back to your rebuild of go2make. I suspect, by reading it, that it does not quite satisfy - I need to also be able to list modules that the k8s "main" module does not depend on, which is where I get the need to list multiple packages and the |
Ideally there would be a flag similar to Maybe @bcmills @jayconrod @matloob have more ideas in terms of how you could piggyback off of Go's own build cache. Telling if a package (or its dependencies) has changed between two points in time seems generally useful, without having to fill the build cache first. Arguably that would be a fairly different caching system, but cmd/go could reuse a lot of code if it wanted to offer such a feature. |
Yeah, I realized this is what is happening, which would maybe be OK if we could re-use the built results, but we have some build flags we pass, so I am not even sure we can. The If not for the When I tried to run your new tool against my model repo (much easier to validate than all of kubernetes) I get:
I will have to debug later. Out of time for now. |
If you've actually built the package using the Otherwise, probably the best you can do with |
Kubernetes has several tools (the go2make one discussed here, as well as code generation tools that make extensive use of If go is planning to drop support for GOPATH in 1.17, have those behavior and performance changes been communicated?
|
Just to explain a bit of what's going on under the hood:
@liggitt To answer your questions directly:
Yes, but probably not well enough. When we published If it would be helpful to add some advice to the
I'm not sure we have anything that specific. @matloob do you know if there's anything like that?
|
FWIW, I don't think that |
As per @heschik in #29452
Converting a tool from
go/build
togo/packages
incurs a huge slowdown. I tried to min-repro it here. Note that we have a full vendor/ dir, no un-vendored deps, and modules are NOT turned on yet.What version of Go are you using (
go version
)?1.12.1
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (
go env
)?linux, amd64
go env
OutputWhat did you do?
Build and run the two programs below and time them.
What did you expect to see?
Roughly the same timing
What did you see instead?
0.008s vs 1.272s
go-build.go:
go-packages.go:
Testing:
The text was updated successfully, but these errors were encountered: