go.tools/cmd/gotype: parse in parallel

Performance increase is much smaller than expected;
need to investigate. Possibly, repeatedly (for each
line) accessing file sets from multiple goroutines
(in the scanner) is a bottle neck; or perhaps i/o.

R=adonovan
CC=golang-dev
https://golang.org/cl/23090043
This commit is contained in:
Robert Griesemer 2013-11-07 13:32:02 -08:00
parent f1e5b03c6e
commit 0c141425f6
2 changed files with 47 additions and 12 deletions

View File

@ -29,10 +29,12 @@ The flags are:
verbose mode
Debugging flags:
-seq
parse sequentially, rather than in parallel
-ast
print AST
print AST (forces -seq)
-trace
print parse trace
print parse trace (forces -seq)
-comments
parse comments (ignored unless -ast or -trace is provided)

View File

@ -15,6 +15,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"time"
"code.google.com/p/go.tools/go/types"
@ -27,8 +28,9 @@ var (
verbose = flag.Bool("v", false, "verbose mode")
// debugging support
printAST = flag.Bool("ast", false, "print AST")
printTrace = flag.Bool("trace", false, "print parse trace")
sequential = flag.Bool("seq", false, "parse sequentially, rather than in parallel")
printAST = flag.Bool("ast", false, "print AST (forces -seq)")
printTrace = flag.Bool("trace", false, "print parse trace (forces -seq)")
parseComments = flag.Bool("comments", false, "parse comments (ignored unless -ast or -trace is provided)")
)
@ -78,11 +80,12 @@ func report(err error) {
errorCount++
}
// parse may be called concurrently
func parse(filename string, src interface{}) (*ast.File, error) {
if *verbose {
fmt.Println(filename)
}
file, err := parser.ParseFile(fset, filename, src, parserMode)
file, err := parser.ParseFile(fset, filename, src, parserMode) // ok to access fset concurrently
if *printAST {
ast.Print(fset, file)
}
@ -98,14 +101,39 @@ func parseStdin() (*ast.File, error) {
}
func parseFiles(filenames []string) ([]*ast.File, error) {
var files []*ast.File
for _, filename := range filenames {
file, err := parse(filename, nil)
if err != nil {
return nil, err
files := make([]*ast.File, len(filenames))
if *sequential {
for i, filename := range filenames {
var err error
files[i], err = parse(filename, nil)
if err != nil {
return nil, err // leave unfinished goroutines hanging
}
}
} else {
type parseResult struct {
file *ast.File
err error
}
out := make(chan parseResult)
for _, filename := range filenames {
go func(filename string) {
file, err := parse(filename, nil)
out <- parseResult{file, err}
}(filename)
}
for i := range filenames {
res := <-out
if res.err != nil {
return nil, res.err // leave unfinished goroutines hanging
}
files[i] = res.file
}
files = append(files, file)
}
return files, nil
}
@ -131,7 +159,6 @@ func parseDir(dirname string) ([]*ast.File, error) {
func getPkgFiles(args []string) ([]*ast.File, error) {
if len(args) == 0 {
// stdin
var file *ast.File
file, err := parseStdin()
if err != nil {
return nil, err
@ -196,10 +223,16 @@ func printStats(d time.Duration) {
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) // remove this once runtime is smarter
flag.Usage = usage
flag.Parse()
if *printAST || *printTrace {
*sequential = true
}
initParserMode()
initSizes()
start := time.Now()
files, err := getPkgFiles(flag.Args())