Skip to content

Custom implementation of sprintf and snprintf functions suitable for embedded environments

Notifications You must be signed in to change notification settings

cennian/sprintf_snprintf

Repository files navigation

Custom sprintf/snprintf Implementation

This project provides a custom implementation of the sprintf and snprintf functions in C, along with tests for functionality and performance comparison against the standard library versions.

Overview

This project provides:

  • Custom implementation of sprintf and snprintf functions
  • Comprehensive test suite for validation
  • Performance benchmarks comparing with standard library implementations
  • Cross-platform support (x86 and ARM Cortex-M)
  • No dynamic memory allocation (suitable for embedded systems)

Project Structure

.
├── Makefile             # Build script for tests
├── perf_test.c          # Performance comparison tests
├── README.md            # This file
├── sprintf_snprintf.c   # Custom sprintf/snprintf implementation
├── sprintf_snprintf.h   # Header file for the custom implementation
└── sprintf_test.c       # Functional comparison tests

(Other files like .o, .elf, .bin, .map, .code-workspace, serial_output.txt are build artifacts or editor configurations)

Performance Comparison

x86 Performance Results

Performance testing on x86 systems shows excellent results compared to the standard library implementation:

Format Type Standard Library Custom Implementation Performance Ratio
Simple Integer 37147 µs 31640 µs 0.85 (15% faster)
Negative Integer 35584 µs 32277 µs 0.91 (9% faster)
Unsigned Integer 36665 µs 33083 µs 0.90 (10% faster)
Hex Value 34642 µs 30446 µs 0.88 (12% faster)
Character 24575 µs 21856 µs 0.89 (11% faster)
String 30814 µs 27471 µs 0.89 (11% faster)
Percent Sign 22262 µs 25427 µs 1.14 (14% slower)
Multiple Values 101350 µs 97779 µs 0.96 (4% faster)
Complex Format 109162 µs 105915 µs 0.97 (3% faster)

Note: Times are total execution time for 1,000,000 function calls on a Linux system

Buffer Size Scaling Test (snprintf) - 100,000 iterations

Buffer Size Standard Library Custom Implementation Performance Ratio
16 26628 µs 24766 µs 0.93 (7% faster)
32 25280 µs 17628 µs 0.70 (30% faster)
64 14009 µs 21358 µs 1.52 (52% slower)
128 16451 µs 16974 µs 1.03 (3% slower)
256 15487 µs 16185 µs 1.05 (5% slower)
512 15188 µs 15732 µs 1.04 (4% slower)
1024 10240 µs 8410 µs 0.82 (18% faster)
2048 14276 µs 10957 µs 0.77 (23% faster)
4096 9020 µs 13683 µs 1.52 (52% slower)

ARM Cortex-M7 Performance Results

Performance testing on STM32F7 (ARM Cortex-M7) shows efficient execution with minimal code size:

Metric Value
Code Size 2120 bytes
Data Size 4 bytes
BSS Size 8452 bytes

Note: BSS size includes test harness buffers, not just the implementation

ARM-Specific Optimizations

The implementation includes several optimizations specifically targeting ARM microcontrollers:

  1. Division Minimization: Division operations are computationally expensive on ARM Cortex-M, so the code minimizes their use in numeric conversions
  2. Direct Memory Access: Optimized for ARM's memory access patterns with fewer data movements
  3. Fixed Buffer Sizes: No dynamic memory allocation, critical for embedded applications
  4. Zero-Case Optimization: Special fast paths for common values like zero
  5. Compact Code Size: Entire implementation fits in about 2KB of code space

Building and Running

The project uses a Makefile for building and running tests on a Linux-like system with GCC.

Functional Tests

To compile and run the functional tests (comparing output with standard library sprintf/snprintf):

make test

This builds an executable from sprintf_snprintf.c and sprintf_test.c and runs it.

Performance Tests

To compile and run the performance tests (comparing execution time with standard library sprintf/snprintf):

make perf

This builds an executable from sprintf_snprintf.c and perf_test.c and runs it.

Cleaning

To remove build artifacts:

make clean

How Performance Tests Were Conducted

x86 Performance Testing

Performance tests on x86 Linux were conducted using the following methodology:

  1. Test Environment: Linux system with performance measurements in microseconds
  2. Timing Method: High-precision timing using gettimeofday() for microsecond accuracy
  3. Test Cases: Nine different format string patterns testing various aspects:
    • Simple and negative integers
    • Unsigned integers
    • Hexadecimal values
    • Character formatting
    • String formatting
    • Percent sign
    • Multiple format specifiers
    • Complex mixed formats
  4. Iterations: Each test case executed 1,000,000 times to get statistically significant results
  5. Buffer Size Testing: Additional tests with varying buffer sizes (16 to 4096 bytes) for snprintf
  6. Comparison Method: Direct comparison with standard library implementation using the same input data

ARM Performance Testing Setup

The ARM performance testing framework was designed for the STM32F7 series microcontrollers:

  1. Timer Infrastructure: Custom high-resolution timer using the SysTick peripheral
    • 1ms base timing with sub-millisecond precision using counter values
    • Interrupt-based tick counting for longer durations
  2. Test Cases: Same nine test cases as the x86 tests but with reduced iterations (10,000)
    • Adapted for embedded constraints while maintaining statistical significance
  3. Output Method: Results would be transmitted via UART in a real deployment
  4. Verification: Each test verifies that both implementations produce identical output
  5. Code Size Measurement: ARM-GCC size tool used to measure actual code and data footprint

Performance Analysis

The custom implementation offers these key advantages:

  • Superior performance for common operations: Faster than the standard library for most formatting operations (8 out of 9 test cases on x86)
  • ARM-optimized code: Specifically designed to perform well on ARM Cortex-M processors
  • Smaller memory footprint: Especially important for embedded systems
  • No dynamic memory allocation: Critical for deterministic performance in real-time systems
  • Predictable performance: Consistent execution time regardless of input complexity

Embedded System Considerations

This implementation is particularly well-suited for:

  • Memory-constrained devices (e.g., ARM Cortex-M0/M3/M4/M7 with limited RAM/flash)
  • Real-time applications requiring deterministic behavior
  • Battery-powered devices where processing efficiency translates to power savings
  • Systems without full C library support

License

MIT License

About

Custom implementation of sprintf and snprintf functions suitable for embedded environments

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published