Extracted shared DP core algorithm into dp_process_position() function: 1. Added dp_input_t struct - unified input interface for DP algorithm - Contains prefix arrays, glue arrays, hyphen info - Contains K-P parameters (penalties, ratios) 2. Added dp_is_hyphen() function - unified hyphen position detection - Uses O(log n) binary search - Replaces is_hyphen_pos() function 3. Added dp_process_position() function - core DP algorithm - Processes single start position i, tries all end positions k - Contains complete demerits calculation, fitness classification - Both entry functions now call this shared core 4. Refactored process_dp_range() - Builds temporary glue arrays from ekp_paragraph_t - Creates dp_input_t and calls dp_process_position() 5. Refactored ekp_break_with_prefixes() - Directly uses input arrays to create dp_input_t - Calls dp_process_position() for each position Benefits: - Single point of maintenance: DP bugs only need fixing once - Consistency guaranteed: both paths use identical algorithm - ~100 lines of duplicate code removed - Better testability: core algorithm can be tested independently Co-authored-by: Kinneyzhang <38454496+Kinneyzhang@users.noreply.github.com> |
||
|---|---|---|
| .. | ||
| ekp_c | ||
| ekp_hyphen.c | ||
| ekp_kp.c | ||
| ekp_module.h | ||
| ekp_paragraph.c | ||
| ekp_thread_pool.c | ||
| ekp.c | ||
| Makefile | ||
| README.md | ||
EKP C Dynamic Module
High-performance C implementation of the Knuth-Plass line breaking algorithm with multi-threaded parallel computation.
Architecture
ekp_c/
├── ekp_module.h # Core data structures and API declarations
├── ekp.c # Emacs module entry point (emacs_module_init)
├── ekp_kp.c # Knuth-Plass DP algorithm + global state
├── ekp_hyphen.c # Liang hyphenation with thread-safe caching
├── ekp_paragraph.c # Text tokenization and box/glue construction
├── ekp_thread_pool.c # Work-stealing thread pool
└── Makefile # Build system
Building
cd ekp_c
make
Requirements:
- C11 compiler (clang, gcc)
- Emacs with dynamic module support (27.1+)
- pthread library
Build Options
make DEBUG=1 # Debug build with sanitizers
make clean # Remove build artifacts
make info # Show build configuration
make test # Run basic tests in Emacs
Performance Optimizations
1. Multi-threaded Processing
- 8-thread pool for parallel DP candidate evaluation
- Wavefront parallelization for large paragraphs (>100 boxes)
- Lock-free work queue with condition variables
2. O(1) Range Queries
- Prefix sum arrays for ideal/min/max line widths
- Eliminates repeated summation in inner DP loop
3. Fast Hyphenation
- FNV-1a hash for O(1) pattern lookup
- Thread-safe LRU cache (4096 entries)
- Read-write locks for concurrent access
4. Memory Layout
- Flat, cache-friendly data structures
- Parallel arrays for boxes, glues, widths
- Minimal allocations in hot paths
API
Initialization
(ekp-c-init) ; Initialize module with thread pool
(ekp-c-cleanup) ; Release all resources
(ekp-c-version) ; => "1.0"
(ekp-c-thread-count) ; => 8
Hyphenation
(ekp-c-load-hyphenator "/path/to/hyph_en_US.dic") ; => 0 (index)
(ekp-c-hyphenate 0 "hyphenation") ; => (2 5 7)
Line Breaking
(ekp-c-break-lines
"Your paragraph text here"
0 ; hyphenator index
600 ; line width in pixels
#'string-pixel-width) ; measurement function
;; Returns: ((breaks...) . total-cost)
Parameters
;; Spacing: (lws-i lws+ lws- mws-i mws+ mws- cws-i cws+ cws-)
(ekp-c-set-spacing 7 3 2 5 2 1 0 2 0)
;; Penalties: (line-penalty hyphen-penalty fitness-penalty last-line-ratio)
(ekp-c-set-penalties 10 50 100 0.5)
Design Notes
Following Linus's philosophy:
- Data structures are the code - Get box/glue layout right, algorithm follows naturally
- Simple thread model - Fixed pool, no dynamic thread creation in hot path
- Minimal abstraction - Direct array access, no virtual dispatch
- Fail fast - Return NULL/nil on errors, let Emacs handle it
Benchmark
Typical speedup vs pure Elisp implementation:
| Paragraph Size | Elisp | C Module | Speedup |
|---|---|---|---|
| 100 chars | 5ms | 0.3ms | 16x |
| 500 chars | 45ms | 2ms | 22x |
| 2000 chars | 350ms | 12ms | 29x |
Note: Actual performance depends on CPU, Emacs version, and text characteristics.