├── README.md └── faster.go /README.md: -------------------------------------------------------------------------------- 1 | # go-faster 2 | 3 | ## Installation 4 | 5 | `go get github.com/bouk/go-faster` 6 | 7 | ## Basic usage 8 | 9 | You can make any go program run faster by using more goroutines and channels! Example: 10 | 11 | ```bash 12 | cat test.go 13 | ``` 14 | ```go 15 | package main 16 | 17 | func a(c int) int { 18 | c += 1 19 | return c 20 | } 21 | 22 | func main() { 23 | println(a(2)) 24 | } 25 | ``` 26 | ```bash 27 | go-faster test.go | tee test.go 28 | ``` 29 | ```go 30 | package main 31 | 32 | func a(c int) chan int { 33 | result := make(chan int) 34 | go func() { 35 | result <- func() int { 36 | c += 1 37 | return c 38 | }() 39 | }() 40 | return result 41 | } 42 | 43 | func main() { 44 | println(<-a(2)) 45 | } 46 | ``` 47 | ```bash 48 | go run test.go 49 | 3 50 | ``` 51 | 52 | Because the speed of a go program is measured in the amount of `go` statements and channels you can make any program faster by simply doing `go-faster `. 53 | 54 | ## Even faster 55 | 56 | There is no limitation on the number of times you can run `go-faster` on your program. 57 | 58 | ```bash 59 | go-faster test.go | tee test.go 60 | ``` 61 | ```go 62 | package main 63 | 64 | func a(c int) chan chan chan int { 65 | result := make(chan chan chan int) 66 | go func() { 67 | result <- func() chan chan int { 68 | result := make(chan chan int) 69 | go func() { 70 | result <- func() chan int { 71 | result := make(chan int) 72 | go func() { 73 | result <- func() int { 74 | c += 1 75 | return c 76 | }() 77 | }() 78 | return result 79 | }() 80 | }() 81 | return result 82 | }() 83 | }() 84 | return result 85 | } 86 | 87 | func main() { 88 | println(<-<-<-a(2)) 89 | } 90 | ``` 91 | 92 | We hope that one day we can have infinite goroutines and channels. 93 | -------------------------------------------------------------------------------- /faster.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "go/format" 7 | "go/parser" 8 | "go/token" 9 | "os" 10 | ) 11 | 12 | var modifiedFunctions = make(map[string]bool) 13 | 14 | func callToReceiveCall(expr *ast.CallExpr) *ast.UnaryExpr { 15 | return &ast.UnaryExpr{Op: token.ARROW, X: expr} 16 | } 17 | 18 | func typeToChanType(t ast.Expr) *ast.ChanType { 19 | return &ast.ChanType{Value: t, Dir: ast.SEND | ast.RECV, Arrow: token.NoPos} 20 | } 21 | 22 | func increaseSpeed(f *ast.FuncDecl) *ast.FuncDecl { 23 | returnType := f.Type.Results.List[0].Type 24 | 25 | res := &ast.FuncDecl{} 26 | res.Body = &ast.BlockStmt{} 27 | res.Doc = f.Doc 28 | res.Name = f.Name 29 | res.Recv = f.Recv 30 | 31 | res.Type = &ast.FuncType{ 32 | Params: f.Type.Params, 33 | Results: &ast.FieldList{ 34 | List: []*ast.Field{ 35 | &ast.Field{ 36 | Type: typeToChanType(returnType), 37 | }, 38 | }, 39 | }, 40 | } 41 | 42 | resultObject := ast.NewObj(ast.Var, "result") 43 | resultIdent := &ast.Ident{ 44 | NamePos: token.NoPos, 45 | Name: "result", 46 | Obj: resultObject, 47 | } 48 | 49 | innerFunc := &ast.FuncLit{ 50 | Body: f.Body, 51 | Type: &ast.FuncType{ 52 | Results: f.Type.Results, 53 | }, 54 | } 55 | 56 | res.Body.List = append(res.Body.List, &ast.AssignStmt{ 57 | Lhs: []ast.Expr{resultIdent}, 58 | TokPos: token.NoPos, 59 | Tok: token.DEFINE, 60 | Rhs: []ast.Expr{ 61 | &ast.CallExpr{ 62 | Fun: ast.NewIdent("make"), 63 | Args: []ast.Expr{typeToChanType(returnType)}, 64 | Ellipsis: token.NoPos, 65 | }, 66 | }, 67 | }) 68 | 69 | res.Body.List = append(res.Body.List, &ast.GoStmt{ 70 | Call: &ast.CallExpr{ 71 | Fun: &ast.FuncLit{ 72 | Type: &ast.FuncType{Params: &ast.FieldList{}}, 73 | Body: &ast.BlockStmt{ 74 | List: []ast.Stmt{ 75 | &ast.SendStmt{ 76 | Chan: resultIdent, 77 | Value: &ast.CallExpr{Ellipsis: token.NoPos, Fun: innerFunc}, 78 | }, 79 | }, 80 | }, 81 | }, 82 | }, 83 | }) 84 | 85 | res.Body.List = append(res.Body.List, &ast.ReturnStmt{Results: []ast.Expr{resultIdent}}) 86 | 87 | return res 88 | } 89 | 90 | type visitor func(ast.Node) 91 | 92 | func (v visitor) Visit(node ast.Node) ast.Visitor { 93 | v(node) 94 | return v 95 | } 96 | 97 | func wrapInReceive(expr ast.Expr) ast.Expr { 98 | if callExpr, ok := expr.(*ast.CallExpr); ok && callExpr.Lparen != token.NoPos { 99 | if ident, ok := callExpr.Fun.(*ast.Ident); ok && modifiedFunctions[ident.Name] { 100 | callExpr.Lparen = token.NoPos 101 | return callToReceiveCall(callExpr) 102 | } 103 | } 104 | return expr 105 | } 106 | 107 | func improveCallExprs(list []ast.Expr) { 108 | for i, node := range list { 109 | list[i] = wrapInReceive(node) 110 | } 111 | } 112 | 113 | func main() { 114 | if len(os.Args) != 2 { 115 | fmt.Fprintf(os.Stderr, "usage: %s [inputfile]\n", os.Args[0]) 116 | return 117 | } 118 | fset := token.NewFileSet() 119 | inputFile, err := parser.ParseFile(fset, os.Args[1], nil, parser.ParseComments) 120 | 121 | if err != nil { 122 | fmt.Fprintf(os.Stderr, "failed to parse %s\n", os.Args[1]) 123 | return 124 | } 125 | 126 | for i, decl := range inputFile.Decls { 127 | if funcDecl, ok := decl.(*ast.FuncDecl); ok && funcDecl.Recv == nil { 128 | if funcDecl.Type.Results != nil && len(funcDecl.Type.Results.List) == 1 { 129 | modifiedFunctions[funcDecl.Name.Name] = true 130 | inputFile.Decls[i] = increaseSpeed(funcDecl) 131 | } 132 | } 133 | } 134 | 135 | for _, decl := range inputFile.Decls { 136 | if funcDecl, ok := decl.(*ast.FuncDecl); ok { 137 | ast.Walk(visitor(func(node ast.Node) { 138 | switch n := node.(type) { 139 | case *ast.Field: 140 | n.Type = wrapInReceive(n.Type) 141 | 142 | case *ast.Ellipsis: 143 | if n.Elt != nil { 144 | n.Elt = wrapInReceive(n.Elt) 145 | } 146 | 147 | case *ast.FuncLit: 148 | 149 | case *ast.CompositeLit: 150 | if n.Type != nil { 151 | n.Type = wrapInReceive(n.Type) 152 | } 153 | improveCallExprs(n.Elts) 154 | 155 | case *ast.ParenExpr: 156 | n.X = wrapInReceive(n.X) 157 | 158 | case *ast.SelectorExpr: 159 | n.X = wrapInReceive(n.X) 160 | 161 | case *ast.IndexExpr: 162 | n.X = wrapInReceive(n.X) 163 | n.Index = wrapInReceive(n.Index) 164 | 165 | case *ast.SliceExpr: 166 | n.X = wrapInReceive(n.X) 167 | if n.Low != nil { 168 | n.Low = wrapInReceive(n.Low) 169 | } 170 | if n.High != nil { 171 | n.High = wrapInReceive(n.High) 172 | } 173 | if n.Max != nil { 174 | n.Max = wrapInReceive(n.Max) 175 | } 176 | 177 | case *ast.TypeAssertExpr: 178 | n.X = wrapInReceive(n.X) 179 | if n.Type != nil { 180 | n.Type = wrapInReceive(n.Type) 181 | } 182 | 183 | case *ast.CallExpr: 184 | n.Fun = wrapInReceive(n.Fun) 185 | improveCallExprs(n.Args) 186 | 187 | case *ast.StarExpr: 188 | n.X = wrapInReceive(n.X) 189 | 190 | case *ast.UnaryExpr: 191 | n.X = wrapInReceive(n.X) 192 | 193 | case *ast.BinaryExpr: 194 | n.X = wrapInReceive(n.X) 195 | n.Y = wrapInReceive(n.Y) 196 | 197 | case *ast.KeyValueExpr: 198 | n.Key = wrapInReceive(n.Key) 199 | n.Value = wrapInReceive(n.Value) 200 | 201 | // Types 202 | case *ast.ArrayType: 203 | if n.Len != nil { 204 | n.Len = wrapInReceive(n.Len) 205 | } 206 | n.Elt = wrapInReceive(n.Elt) 207 | 208 | case *ast.MapType: 209 | n.Key = wrapInReceive(n.Key) 210 | n.Value = wrapInReceive(n.Value) 211 | 212 | case *ast.ChanType: 213 | n.Value = wrapInReceive(n.Value) 214 | 215 | case *ast.ExprStmt: 216 | n.X = wrapInReceive(n.X) 217 | 218 | case *ast.SendStmt: 219 | n.Chan = wrapInReceive(n.Chan) 220 | n.Value = wrapInReceive(n.Value) 221 | 222 | case *ast.IncDecStmt: 223 | n.X = wrapInReceive(n.X) 224 | 225 | case *ast.AssignStmt: 226 | improveCallExprs(n.Lhs) 227 | improveCallExprs(n.Rhs) 228 | 229 | case *ast.ReturnStmt: 230 | improveCallExprs(n.Results) 231 | 232 | case *ast.IfStmt: 233 | n.Cond = wrapInReceive(n.Cond) 234 | 235 | case *ast.CaseClause: 236 | improveCallExprs(n.List) 237 | 238 | case *ast.SwitchStmt: 239 | if n.Tag != nil { 240 | n.Tag = wrapInReceive(n.Tag) 241 | } 242 | 243 | case *ast.ForStmt: 244 | if n.Cond != nil { 245 | n.Cond = wrapInReceive(n.Cond) 246 | } 247 | 248 | case *ast.RangeStmt: 249 | n.Key = wrapInReceive(n.Key) 250 | if n.Value != nil { 251 | n.Value = wrapInReceive(n.Value) 252 | } 253 | n.X = wrapInReceive(n.X) 254 | 255 | case *ast.ValueSpec: 256 | if n.Type != nil { 257 | n.Type = wrapInReceive(n.Type) 258 | } 259 | improveCallExprs(n.Values) 260 | 261 | case *ast.TypeSpec: 262 | n.Type = wrapInReceive(n.Type) 263 | 264 | } 265 | }), funcDecl) 266 | } 267 | } 268 | 269 | format.Node(os.Stdout, fset, inputFile) 270 | } 271 | --------------------------------------------------------------------------------