### OpenACS and Tcl/Tk Conference 2026, July 17-18

- Andreas Kupries
- AKTIVE

- AK Tcl Image Vector Extension
- MIT/BSD Licensed
- Inspired by VIPS
- Mixes C and Tcl via Critcl

### AK Tcl Image Vector Extension

- Last year: Introduced by use,
  light on internals

- See [PDF] [ZIP] [WU Lecturecast]
  (Entire conference)

  https://core.tcl-lang.org/akupries/aktive/doc/trunk/doc/presentations/eurotcl-2025-bologna.pdf

  https://core.tcl-lang.org/akupries/aktive/doc/trunk/doc/presentations/eurotcl-2025-bologna.zip

  https://learn.wu.ac.at/eurotcl2025/lecturecasts/753988258?m=delivery

  https://learn.wu.ac.at/eurotcl2025/lecturecasts/?page=1

### AK Tcl Image Vector Extension

- Last year: Introduction by use,
  light on internals
- Today: A deeper look at the internals
  - Demand-driven: Lazy construction
    - Data structures (definition, run)
  - Horizontally Threaded Execution
    - Thread coordination, ordered and not
- Benchmarking & Performance
  - Loop unrolling
  - (SIMD) Vectorization

### AK Tcl Image Vector Extension

#### Lazy construction

- Image creation builds definition structures
  - Linear pipe
  - Tree
  - Directed acyclic graph

### AK Tcl Image Vector Extension

#### Lazy construction

- Image materialization builds execution
  structures
  - ... computes things
  - ... and tears the execution structures down again
  - The definition structures stay

### AK Tcl Image Vector Extension

#### Lazy construction

- Examples of materializators
  - Saving pixels to file, channel, memory, ...
  - Computing whole-image statistics

### AK Tcl Image Vector Extension

#### Preface: Inspection

    aktive format as d2        $image
    aktive format as markdown  $image
    aktive format as tclscript $image

    set file1 [aktive read from netpbm file path ...];# FO(2): tmp2,tmp4
    set tmp2 [aktive op view $file1 port {-1 -1 382 252}]
    set tmp3 [aktive op tile max $tmp2 radius 1]
    set tmp4 [aktive op view $file1 port {-1 -1 382 252}]
    set tmp5 [aktive op tile min $tmp4 radius 1]
    set tmp6 [aktive op math sub $tmp3 $tmp5]
    set tmp7 [aktive op math1 invert $tmp6]
    set tmp8 [aktive op math1 gamma expand $tmp7]
    set tmp9 [aktive op color scRGB to Grey $tmp8]
    set result [aktive op math1 scale $tmp9 factor 0.01]

### AK Tcl Image Vector Extension

#### Preface: Inspection

    aktive format as d2        $image
    aktive format as markdown  $image
    aktive format as tclscript $image

Charcoal effect

full size (assets/charcoal.svg)

### AK Tcl Image Vector Extension

#### Definition Structures

Shoutout: Pikchr @ https://pikchr.org

- type (function vectors, parameter defs)
- parameters
- srcs
- domain (geometry)
- state

### AK Tcl Image Vector Extension

#### Definition Structures

- type (function vectors, parameter defs)
- content (parameters, srcs, domain, state)
- metadata
- refcount

### AK Tcl Image Vector Extension

#### Definition Structures

Types and Parameters

### AK Tcl Image Vector Extension

#### Definition Structures

Content and Meta

### AK Tcl Image Vector Extension

#### Definition Structures

C Types

### AK Tcl Image Vector Extension

#### Runtime Structures

- (runtime) state
- (runtime) srcs
- result buffer
- image
  - param
  - type
  - domain
  - state

### AK Tcl Image Vector Extension

#### Runtime Structures

- region: result buffer, image, type
- region_info: runtime state, runtime srcs, param, domain, def state
- context

### AK Tcl Image Vector Extension

#### Runtime Structures

### AK Tcl Image Vector Extension

#### Runtime Structures

### AK Tcl Image Vector Extension

#### Horizontally Threaded Execution

- Generate tasks
- Process tasks
- Collect results

### AK Tcl Image Vector Extension

#### Horizontally Threaded Execution

- Generate tasks --- Maker
- Process tasks
- Collect results

### AK Tcl Image Vector Extension

#### Horizontally Threaded Execution

- Generate tasks --- Maker
- Process tasks --- Workers
- Collect results

### AK Tcl Image Vector Extension

#### Horizontally Threaded Execution

- Generate tasks --- Maker
- Process tasks --- Workers
- Collect results --- Completer

### AK Tcl Image Vector Extension

#### Horizontally Threaded Execution

- Generate tasks --- Maker
- Process tasks --- Workers

- Here are the runtime structures

- Collect results --- Completer

Coordination ?

- Unordered
- Sequential

### AK Tcl Image Vector Extension

#### Horizontally Threaded Execution

Unordered

### AK Tcl Image Vector Extension

#### Horizontally Threaded Execution

Sequential

### AK Tcl Image Vector Extension

#### Benchmarking & Performance

    op-bench {
        aktive image from value // width %W height %H depth 1 value 0.5
    }

### AK Tcl Image Vector Extension

#### Benchmarking & Performance

        foreach sink {
    	null
    	null-s
        } {
    	# varying concurrency ... unthreaded, ...
    	#                     ... threads up to num cpu cores, ...
    	#                     ... then overcommitted cpu ...
    	foreach cores {
    	    -1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
    	} {
    	    # varying sizes ... small to large
    	    foreach size {
    		1 10 100 1000 10000 100000 1000000 10000000
    	    } { ;#       1K   10K   100K   1M      10M
    		# varying shapes of the same size ... tall to wide
    		for { set h $size ; set w 1 } { $h > 0 } {
    		    set w [expr {$w*10}] ; set h [expr {$h/10}]
    		} { ...

### AK Tcl Image Vector Extension

#### Benchmarking & Performance

Unordered vs. Sequential

### AK Tcl Image Vector Extension

#### Benchmarking & Performance

Unordered vs. Sequential

### AK Tcl Image Vector Extension

#### Benchmarking & Performance

- Currently: Performance through threading
- Can we gain more ? How ?
- Make the workers themselves faster!

### AK Tcl Image Vector Extension

#### Benchmarking & Performance

- Make the workers themselves faster!

- Idea 1: Loop unrolling

- Out of Order Superscalar

- Idea 2: Vectorization (SIMD)

- x86 SSE*, AVX*, Arm NEON, SVE, RISC-V "V"
- Google Highway (Portable, Length-Agnostic)
  @ https://github.com/google/highway

### AK Tcl Image Vector Extension

Function specification

    set unary0 {
        acos           { scalar { v = acos  (v);     } }
        [...]
    }

    set unary1 {
        [...]
        shift  {
    	scalar  { v = v + a;      }
    	highway { v = Add (v, a); }
        }
        [...]
    }

### AK Tcl Image Vector Extension

Templating

    set u4_unary0_vdecl {
        extern void aktive_vector4_unary_@name@ (
            double* d, double* s, aktive_uint n);
    }
    set u4_unary0_vdef {
        void aktive_vector4_unary_@name@ (double* d, double* s, aktive_uint n) {
    	// 4-unroll
     	for (; n > 3; n -= 4, d += 4, s += 4) {
    	    double v0 = s[0]; // v1, v2, v3 = s[1,2,3]
    #define v v0 // ... v1 v2 v3
    	    @opcode@
    #undef  v
    	    d[0] = v0; // d[1,2,3] = v1, v2, v3
    	}
    	// 2-unroll,
     	for (; n > 1; n -= 2, d += 2, s += 2) { ... }
    	// remainder
     	for (; n > 0; n--, d++, s++) { ... }
        }
    }

### AK Tcl Image Vector Extension

Templating

    set u1_unary0_hdecl {
        void aktive_highway1_unary_@name@ (
            double* d, double* s, aktive_uint n);}
    set u1_unary0_hdef  {
        HWY_ATTR void aktive_highway1_unary_@name@ (
            double* d, double* s, aktive_uint n) {
    	const int32_t N = Lanes(f64);
    	@decls@
    	if (n < N) {	// not a full block - do a masked partial op
    	    auto mask = FirstN(f64, n);
    	    auto v    = MaskedLoad(mask, f64, s);
    	    @opcode@
    	    BlendedStore (v, mask, f64, d);	    return;
    	}
    	aktive_uint k, border = n - N; 	// lane-sized blocks
    	for (k = 0; k < n; k += N) {
    	    aktive_uint at = HWY_MIN (k, border);
    	    auto v = LoadU (f64, s + at);
    	    @opcode@
    	    StoreU (v, f64, d + at);
    	}
        }
    }

### AK Tcl Image Vector Extension

Templating

    set u1_unary0_hdecl {
        void aktive_highway1_unary_@name@ (
            double* d, double* s, aktive_uint n);}
    set u1_unary0_hdef  {
        HWY_ATTR void aktive_highway1_unary_@name@ (
            double* d, double* s, aktive_uint n) {
    	const int32_t N = Lanes(f64);
    	@decls@
    	if (n < N) {	// not a full block - do a masked partial op
    	    auto mask = FirstN(f64, n);
    	    auto v    = MaskedLoad(mask, f64, s);
    	    @opcode@
    	    BlendedStore (v, mask, f64, d);	    return;
    	}
    	aktive_uint k, border = n - N; 	// lane-sized blocks
    	for (k = 0; k < n; k += N) {
    	    aktive_uint at = HWY_MIN (k, border);
    	    auto v = LoadU (f64, s + at);
    	    @opcode@
    	    StoreU (v, f64, d + at);
    	}
        }
    }

### AK Tcl Image Vector Extension

#### Benchmarking & Performance

Loop Unrolling (1x, 2x, 4x) -- acos

### AK Tcl Image Vector Extension

#### Benchmarking & Performance

Loop Unrolling (1x, 2x, 4x) -- shift

### AK Tcl Image Vector Extension

#### Benchmarking & Performance

Loop Unrolling 4x vs Highway -- shift

https://core.tcl-lang.org/akupries/aktive/doc/highway-simd-experiment/bench/plots/simd/README.md

https://core.tcl-lang.org/akupries/aktive/timeline?r=highway-simd-experiment

### AK Tcl Image Vector Extension

#### Benchmarking & Performance

Benchmarks

https://core.tcl-lang.org/akupries/aktive/doc/trunk/bench/plots/README.md

SIMD Benchmarks

https://core.tcl-lang.org/akupries/aktive/doc/highway-simd-experiment/bench/plots/README.md

### AK Tcl Image Vector Extension

- Questions ? ...