Doubt Branchless code

matheus matheus at gmail.com
Tue Apr 7 22:10:33 UTC 2026


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).


More information about the Digitalmars-d-learn mailing list