diff --git a/doc/docs.md b/doc/docs.md index 4e692858a5..e3d7fbb593 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -1684,6 +1684,22 @@ vm := vmod.decode( @VMOD_FILE ) or { panic(err) } eprintln('$vm.name $vm.version\n $vm.description') ``` +## Performance tuning + +The generated C code is usually fast enough. Although rarely, there +are some situations though, where you want to give additional hints +to the C compiler, so that it can further optimize some blocks of code. +NB: these are *rarely* needed, and should not be used, unless you +profile your code, and see that there are significant benefits for them. + +[inline] - you can tag functions with [inline], and the C compiler will +try to inline them, which in some cases, may be beneficial for peformance. + +`if _likely_(bool expression) {` this hints the C compiler, that the passed +boolean expression is very likely to be true, so it can generate assembly +code, with less chance of branch misprediction. In the JS backend, +that does nothing. + ## Reflection via codegen Having built-in JSON support is nice, but V also allows you to create efficient diff --git a/vlib/builtin/cfns.c.v b/vlib/builtin/cfns.c.v index 12942404f8..aaaf512ae5 100644 --- a/vlib/builtin/cfns.c.v +++ b/vlib/builtin/cfns.c.v @@ -1,10 +1,5 @@ module builtin -// See cheaders.v: _likely_ is actually a macro, to hint the C compiler -// that the passed boolean expression is very likely to be true, so it -// can generate assembly code, with less chance of branch misprediction. -fn C._likely_(bool) bool - // fn C.memcpy(byteptr, byteptr, int) voidptr diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 49eafd615f..7af4ae3f99 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -13,7 +13,7 @@ pub type Expr = AnonFn | ArrayInit | AsCast | AssignExpr | Assoc | BoolLiteral | CastExpr | CharLiteral | ComptimeCall | ConcatExpr | EnumVal | FloatLiteral | Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | MapInit | MatchExpr | None | OrExpr | ParExpr | PostfixExpr | PrefixExpr | RangeExpr | SelectorExpr | SizeOf | StringInterLiteral | - StringLiteral | StructInit | Type | TypeOf + StringLiteral | StructInit | Type | TypeOf | Likely pub type Stmt = AssertStmt | AssignStmt | Attr | Block | BranchStmt | Comment | CompIf | ConstDecl | DeferStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | GlobalDecl | GoStmt | @@ -755,6 +755,12 @@ pub: type_name string } +pub struct Likely { +pub: + expr Expr + pos token.Position +} + pub struct TypeOf { pub: expr Expr @@ -888,6 +894,9 @@ pub fn (expr Expr) position() token.Position { StructInit { return it.pos } + Likely { + return it.pos + } // ast.TypeOf { } else { return token.Position{} diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index ff77da7eb8..8aa4760672 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -163,6 +163,9 @@ pub fn (x Expr) str() string { TypeOf { return 'typeof(${it.expr.str()})' } + Likely { + return '_likely_(${it.expr.str()})' + } else { return '[unhandled expr type ${typeof(x)}]' } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index c4ca5c16a5..c1557a12e7 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1772,7 +1772,7 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { c.error('checker: too many expr levels: $c.expr_level ', node.position()) return table.void_type } - + match mut node { ast.AnonFn { keep_fn := c.cur_fn @@ -1944,6 +1944,14 @@ pub fn (mut c Checker) expr(node ast.Expr) table.Type { it.expr_type = c.expr(it.expr) return table.string_type } + ast.Likely { + ltype := c.expr(it.expr) + if !c.check_types(ltype, table.bool_type) { + ltype_sym := c.table.get_type_symbol(ltype) + c.error('`_likely_()` expects a boolean expression, instead it got `${ltype_sym.name}`', it.pos) + } + return table.bool_type + } else { tnode := typeof(node) if tnode != 'unknown v.ast.Expr' { diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 5443d38146..06f9417b09 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -734,6 +734,11 @@ pub fn (mut f Fmt) expr(node ast.Expr) { f.expr(it.expr) f.write(')') } + ast.Likely { + f.write('_likely_(') + f.expr(it.expr) + f.write(')') + } } } diff --git a/vlib/v/gen/cgen.v b/vlib/v/gen/cgen.v index 7658a9114f..475c20a60b 100644 --- a/vlib/v/gen/cgen.v +++ b/vlib/v/gen/cgen.v @@ -1586,6 +1586,11 @@ fn (mut g Gen) expr(node ast.Expr) { ast.TypeOf { g.typeof_expr(it) } + ast.Likely { + g.write('_likely_(') + g.expr(it.expr) + g.write(')') + } else { // #printf("node=%d\n", node.typ); println(term.red('cgen.expr(): bad node ' + typeof(node))) diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 023b864cc5..8c3121a040 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -627,6 +627,11 @@ fn (mut g JsGen) expr(node ast.Expr) { // skip: JS has no types // TODO maybe? } + ast.Likely { + g.write('(') + g.expr(it.expr) + g.write(')') + } ast.TypeOf { g.gen_typeof_expr(it) // TODO: Should this print the V type or the JS type? diff --git a/vlib/v/parser/pratt.v b/vlib/v/parser/pratt.v index 8ba46dbac5..1051ad2eb6 100644 --- a/vlib/v/parser/pratt.v +++ b/vlib/v/parser/pratt.v @@ -103,6 +103,17 @@ pub fn (mut p Parser) expr(precedence int) ast.Expr { expr: expr } } + .key_likely { + p.next() + p.check(.lpar) + lpos := p.tok.position() + expr := p.expr(0) + p.check(.rpar) + node = ast.Likely{ + expr: expr + pos: lpos + } + } .lcbr { // Map `{"age": 20}` or `{ x | foo:bar, a:10 }` p.next() diff --git a/vlib/v/tests/supports__likely__test.v b/vlib/v/tests/supports__likely__test.v new file mode 100644 index 0000000000..555cc60925 --- /dev/null +++ b/vlib/v/tests/supports__likely__test.v @@ -0,0 +1,24 @@ +// _likely_(expr) should be compilable, and it should return the expr + +fn test_likely_type(){ + assert typeof(_likely_(false)) == 'bool' +} + +fn test_likely(){ + if _likely_(2<10) { + assert true + eprintln('ok, happens every time') + } else { + eprintln('happens *infrequently*') + assert false + } +} + +fn test_likely_returns_the_value_of_its_bool_argument(){ + i := 123 + if _likely_(i<2) { + assert false + } else { + assert true + } +} diff --git a/vlib/v/token/token.v b/vlib/v/token/token.v index 502ded7353..6f338faba7 100644 --- a/vlib/v/token/token.v +++ b/vlib/v/token/token.v @@ -112,6 +112,7 @@ pub enum Kind { key_return key_select key_sizeof + key_likely key_offsetof key_struct key_switch @@ -218,6 +219,7 @@ fn build_token_str() []string { s[Kind.key_return] = 'return' s[Kind.key_module] = 'module' s[Kind.key_sizeof] = 'sizeof' + s[Kind.key_likely] = '_likely_' s[Kind.key_go] = 'go' s[Kind.key_goto] = 'goto' s[Kind.key_const] = 'const'