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

x/tools/imports: support sessions in Process and FixImports for performance #66802

Open
philip-peterson opened this issue Apr 12, 2024 · 7 comments
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Tools This label describes issues relating to any tools in the x/tools repository.
Milestone

Comments

@philip-peterson
Copy link

philip-peterson commented Apr 12, 2024

Proposal Details

Certain toolchains such as Ent make extensive use of imports.Process(...) within a loop to format multiple files in a directory. However, because each invocation of Process itself executes a loop via parseOtherFiles on multiple files in the directory as well, the complexity may reach O(N^2) in this use case.

As a result, this proposal is to introduce support for a session object passed to Process:

func Process(s *Session, filename string, src []byte, opt *Options) (formatted []byte, err error) {
   ...
}

A session would a thin wrapper around an LRU cache, which can use the existing LRU functionality in gopls/internal/util/lru (which could easily be relocated to internal/lru):

type Session struct {
	Cache *lru.Cache
	Fset  *token.FileSet
}

Usage would look like:

s := NewSession(10) // 10 MB cache
return Process(s, file, contents, opts)

The LRU key would be something like (file path, file contents) and value would be the parsed AST. This would help prevent repeated calls to parse from incurring significant performance cost.

Session would be allowed to be nil to support the current use-case.

@ianlancetaylor
Copy link
Contributor

x/tools/internal is not visible API, so this does not need to go through the proposal process.

@ianlancetaylor ianlancetaylor changed the title proposal: x/tools/internal/imports: Support sessions in Process and FixImports for performance x/tools/internal/imports: support sessions in Process and FixImports for performance Apr 14, 2024
@ianlancetaylor ianlancetaylor modified the milestones: Proposal, Unreleased Apr 14, 2024
@ianlancetaylor
Copy link
Contributor

CC @golang/tools-team

@philip-peterson philip-peterson changed the title x/tools/internal/imports: support sessions in Process and FixImports for performance x/tools/imports: support sessions in Process and FixImports for performance Apr 14, 2024
@philip-peterson
Copy link
Author

@ianlancetaylor My mistake, it would also affect the API for x/tools/imports (non-internal). x/tools/imports has a function called Process that invokes another function called Process in x/tools/internal/imports.

@philip-peterson
Copy link
Author

Amendment: The cache, instead of being passed as first positional argument to Process, may be passed through the existing opt *Options argument as a field in Options. This will allow existing code to remain unchanged, since the value will default to nil.

@adonovan
Copy link
Member

cc @findleyr, who has been working in imports recently. We have been discussing a possible imports API that allows the read/parse/infer/fix/format operations to be expressed as composable functions with no inherent dependency on external state. Currently the step I am calling "infer" runs the module resolver against GOMODCACHE to produce and rank candidates; many clients (including gopls) want to be able to hook this step into their own data structure, for more deterministic or customizable behavior. (GOMODCACHE is an accumulation of the user's previous habits.)

@findleyr
Copy link
Contributor

@pjweinb is actually doing some work on goimports at the moment.

Indeed, this sounds like it may align with the needs of gopls.

I worked on goimports performance in #59216, and while I made some improvements for gopls, I also concluded that goimports scanning and resolution should really be re-thought from scratch. Inside gopls, we re-use an internal/imports.ProcessEnv, but I don't think the fix is to expose that type from x/tools/imports--we should instead come up with a new API that is less complex.

One of the requirements of this new API would be that gopls can itself provide package information as input into the goimports algorithm. Right now, package resolution is hidden behind a (quite complex) Resolver type, which has its own heuristics for scanning the module cache, and which may not agree with gopls about the best packages or modules to import. See also #36077.

@cherrymui cherrymui added NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Tools This label describes issues relating to any tools in the x/tools repository. labels Apr 15, 2024
@philip-peterson
Copy link
Author

@adonovan @findleyr It's exciting that is happening. It sounds like a lot of scope though, and I wonder if it's a separate task from optimizing the current flow? (This ticket would simply be for memoizing the existing logic.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. Tools This label describes issues relating to any tools in the x/tools repository.
Projects
None yet
Development

No branches or pull requests

7 participants
@philip-peterson @ianlancetaylor @adonovan @gopherbot @cherrymui @findleyr and others