digitalmars.D.learn - Doubt Branchless code
- matheus (18/18) Apr 06 Hello,
- matheus (216/217) Apr 07 I ask deepseek and got the answer and sharing for the future:
- Richard (Rikki) Andrew Cattermole (19/47) Apr 07 How I typically do it:
- matheus (5/52) Apr 07 But this is CT, my problem was to this during RT.
- Richard (Rikki) Andrew Cattermole (4/62) Apr 07 It is optimizing RT, but we use CT to factor out the conditionals.
Hello,
Back in C days I used to write code with function pointer to
avoid writing branches inside loops,
To avoid this:
for(;;){
if(opt == 1){ func_1(); }else{func_2()};
}
Or maybe using switch if there were more options...
So I used to do:
if(opt == 1){ fp = &func_1(); }else{ fp = &func_2()};
for(;;){
fp();
}
I would like to know if there is a different way of doing that in
D during RT execution and dynamic change of function execution
before the loops?
Thanks in advance,
Matheus.
Apr 06
On Monday, 6 April 2026 at 19:08:53 UTC, matheus wrote:...I ask deepseek and got the answer and sharing for the future: Calling Functions Without Branches in D In D, you can call functions without branches using several techniques. Here's a comprehensive guide: 1. Function Pointer Tables (Direct Lookup) The most common branchless function calling method - use an array index instead of conditionals. d import std.stdio; // Define functions int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } int div(int a, int b) { return b != 0 ? a / b : 0; } void main() { // Array of function pointers - branchless dispatch int delegate(int, int)[] operations = [&add, &sub, &mul, &div]; int opCode = 2; // 0=add, 1=sub, 2=mul, 3=div // NO branch - direct table lookup and call int result = operations[opCode](10, 5); writeln(result); // 50 (mul) } 2. Using final Switch (Compile-Time Optimized) D's final switch with an enum can be optimized to a jump table: d import std.stdio; enum Operation { add, sub, mul, div } int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } int div(int a, int b) { return b != 0 ? a / b : 0; } int execute(Operation op, int a, int b) { final switch (op) { // Compiler can optimize to jump table case Operation.add: return add(a, b); case Operation.sub: return sub(a, b); case Operation.mul: return mul(a, b); case Operation.div: return div(a, b); } } void main() { // The compiler may generate branchless code here int result = execute(Operation.mul, 10, 5); writeln(result); // 50 } 3. Template-Based Compile-Time Selection Zero runtime overhead - function selection happens at compile time: d import std.stdio; int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } // Template that selects function at compile time auto execute(alias Func, T...)(T args) { return Func(args); } void main() { // These calls are resolved at compile time - NO branches int r1 = execute!add(10, 5); // Direct call to add int r2 = execute!sub(10, 5); // Direct call to sub int r3 = execute!mul(10, 5); // Direct call to mul writeln(r1, " ", r2, " ", r3); // 15 5 50 } 4. Delegate Arrays with Compile-Time Construction Pre-build delegate arrays for OOP-style branchless dispatch: d import std.stdio; class Handler { void process(int x) { writeln("Handler: ", x); } } class Logger { void process(int x) { writeln("Logging: ", x); } } void main() { auto handler = new Handler(); auto logger = new Logger(); // Array of delegates - branchless dispatch void delegate(int)[] processors = [ (int x) => handler.process(x), (int x) => logger.process(x) ]; int type = 1; // Select at runtime // NO if/else - direct table lookup processors[type](42); // Output: Logging: 42 } 5. Using std.functional for Compile-Time Dispatch d import std.functional; import std.stdio; int add(int a, int b) { return a + b; } int mul(int a, int b) { return a * b; } // Compose functions at compile time alias Add5 = curry!(add, 5); alias Mul10 = curry!(mul, 10); void main() { // All resolved at compile time - branchless int r1 = Add5(3); // 8 int r2 = Mul10(7); // 70 writeln(r1, " ", r2); } 6. Virtual Function Table (VTBL) - Already Branchless D's classes use vtables internally, which is naturally branchless: d import std.stdio; interface Operation { int execute(int a, int b); } class Add : Operation { int execute(int a, int b) { return a + b; } } class Mul : Operation { int execute(int a, int b) { return a * b; } } void main() { Operation op; int choice = 1; // Runtime decision // This if is the ONLY branch (to select object) if (choice == 0) op = new Add(); else op = new Mul(); // The virtual call itself is branchless (vtable lookup) int result = op.execute(10, 5); // No branch here! writeln(result); // 50 } 7. Function Pointer from Associative Array For sparse or non-sequential selections: d import std.stdio; int add(int a, int b) { return a + b; } int mul(int a, int b) { return a * b; } int power(int a, int b) { int r = 1; foreach(_; 0..b) r *= a; return r; } void main() { // Associative array for string-based lookup int delegate(int, int)[string] ops = [ "add": &add, "mul": &mul, "pow": &power ]; string opName = "pow"; // Branchless hash lookup + call int result = ops[opName](2, 3); writeln(result); // 8 } Performance Comparison d import std.stdio; import std.datetime; int add(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } int mul(int a, int b) { return a * b; } // Branch version int withBranches(int op, int a, int b) { if (op == 0) return add(a, b); if (op == 1) return sub(a, b); return mul(a, b); } // Branchless table version int delegate(int, int)[] table = [&add, &sub, &mul]; int branchless(int op, int a, int b) { return table[op](a, b); } void main() { const ITERS = 100_000_000; // Test branch version auto sw = StopWatch.Auto.start; int sum1; foreach (i; 0..ITERS) sum1 += withBranches(i % 3, 10, 5); auto time1 = sw.peek; // Test branchless version sw.reset(); int sum2; foreach (i; 0..ITERS) sum2 += branchless(i % 3, 10, 5); auto time2 = sw.peek; writeln("Branch version: ", time1); writeln("Branchless table: ", time2); } Key Takeaway Function pointer tables (delegate[] or function[]) are the most straightforward way to call functions without branches in D. They replace: d if (op == 0) add(); else if (op == 1) sub(); // BAD: branches else mul(); With d: table[op](); // GOOD: branchless This works because array indexing is a constant-time operation with no conditional branches, and the indirect function call through the pointer/delegate is also branchless (the CPU just loads the address and jumps).
Apr 07
On 07/04/2026 7:08 AM, matheus wrote:
Hello,
Back in C days I used to write code with function pointer to avoid
writing branches inside loops,
To avoid this:
for(;;){
if(opt == 1){ func_1(); }else{func_2()};
}
Or maybe using switch if there were more options...
So I used to do:
if(opt == 1){ fp = &func_1(); }else{ fp = &func_2()};
for(;;){
fp();
}
I would like to know if there is a different way of doing that in D
during RT execution and dynamic change of function execution before the
loops?
Thanks in advance,
Matheus.
How I typically do it:
```d
void outer() {
void inner(bool a, bool b)() {
for(;b;) {
static if (a) {}
}
}
if (cond1 && !cond2)
inner!(true, false);
else if (cond2 && !cond1)
inner!(false, true);
else if (cond1 && cond2)
inner!(true, true);
else
inner!(false, false);
}
```
Apr 07
On Tuesday, 7 April 2026 at 23:48:37 UTC, Richard (Rikki) Andrew Cattermole wrote:On 07/04/2026 7:08 AM, matheus wrote:But this is CT, my problem was to this during RT. Thanks anyway for responding, Matheus.Hello, Back in C days I used to write code with function pointer to avoid writing branches inside loops, To avoid this: for(;;){ if(opt == 1){ func_1(); }else{func_2()}; } Or maybe using switch if there were more options... So I used to do: if(opt == 1){ fp = &func_1(); }else{ fp = &func_2()}; for(;;){ fp(); } I would like to know if there is a different way of doing that in D during RT execution and dynamic change of function execution before the loops? Thanks in advance, Matheus.How I typically do it: ```d void outer() { void inner(bool a, bool b)() { for(;b;) { static if (a) {} } } if (cond1 && !cond2) inner!(true, false); else if (cond2 && !cond1) inner!(false, true); else if (cond1 && cond2) inner!(true, true); else inner!(false, false); } ```
Apr 07
On 08/04/2026 12:18 PM, matheus wrote:On Tuesday, 7 April 2026 at 23:48:37 UTC, Richard (Rikki) Andrew Cattermole wrote:It is optimizing RT, but we use CT to factor out the conditionals. I've done this specifically when I would otherwise have RT if statements in loops.On 07/04/2026 7:08 AM, matheus wrote:But this is CT, my problem was to this during RT. Thanks anyway for responding, Matheus.Hello, Back in C days I used to write code with function pointer to avoid writing branches inside loops, To avoid this: for(;;){ if(opt == 1){ func_1(); }else{func_2()}; } Or maybe using switch if there were more options... So I used to do: if(opt == 1){ fp = &func_1(); }else{ fp = &func_2()}; for(;;){ fp(); } I would like to know if there is a different way of doing that in D during RT execution and dynamic change of function execution before the loops? Thanks in advance, Matheus.How I typically do it: ```d void outer() { void inner(bool a, bool b)() { for(;b;) { static if (a) {} } } if (cond1 && !cond2) inner!(true, false); else if (cond2 && !cond1) inner!(false, true); else if (cond1 && cond2) inner!(true, true); else inner!(false, false); } ```
Apr 07









matheus <matheus gmail.com> 