Wt embedded

Find here information on running Wt in resource constrained embedded systems: performance, code size, memory usage, and other information.

General

Wt can easily be built for and deployed on embedded POSIX systems, such as embedded Linux.

Cross-building

Using CMake with a cross compilation environment: to be completed...

Instructions for cross compiling with cmake can be found on the CMake Wiki.

Wt user Alistair of QuickForge has written a blog about cross-compiling for ARM on Windows, and uses Wt as an example in his blog post Exploration of Cross-Compiling on Windows for ARM Linux Distributions

Optimizing executable size

Points to consider when optimizing the executable size.

For building Boost:
  • Use a static build of Boost, which allows the linker to strip away unused symbols
  • Use the following compile flags for Boost:
    • -fvisibility=hidden -fvisibility-inlines-hidden: to avoid exporting symbols in the executable
    • -ffunction-sections -fdata-sections: to allowing fine-grained garbage collection of unused functions/data
For building Wt:
  • Choose build-type MinSizeRel
  • Extra compile flags (CMAKE_CXX_FLAGS)
    • -fvisibility=hidden -fvisibility-inlines-hidden: to avoid exporting symbols in the executable
    • -ffunction-sections -fdata-sections: to allow fine-grained garbage collection of unused functions/data
    • -DHAVE_GNU_REGEX: to avoid the dependency on libboost_regex, when building on a system that is based on glibc or uClibc
    • -DWT_NO_LAYOUT: to avoid pulling in the Wt's layout managers, if you are not using any WLayout classes
    • -DWT_NO_SPIRIT: to avoid depending on spirit to parse locale and cookies (if you don't need that)
    • -DWT_NO_XSS_FILTER: to avoid the extra (runtime) overhead of XSS filtering, usually not relevant for a trusted embedded platform
  • Build static libraries (for libwt.a and libwthttp.a)
    • in CMake: SHARED_LIBS:BOOL=OFF
  • Disable build options you don't need and introduce extra dependencies (libz, openssl ?)
  • Further tune your linker command:
    • Append -v to the linker command used by CMake to see the raw collect2 command-line.
    • By default, shared/static libraries is all-or-nothing with CMake. However, you probably want to use system-wide versions of libstdc++, libm and libc depending on other applications on your device.
      • Use -Bdynamic in front of libraries you wish to link dynamically against
    • There are some other flags that you need to use to make sure the linker does not keep unused symbols:
      • Remove -export-dynamic
      • Add --gc-sections
  • Strip your binary using strip -s.
  • Optionally, when available for your platform, you may want to compress the size of your binary using the Ultimate Packer for eXecutables. This typically reduces executable size further by 60-70%, without noticable run-time performance hits.

Measuring performance

To report the run-time performance of Wt on a particular embedded platform, you must connect to the device using a local area connection (through at most one switch), and measure the time between transmission and reception of packets (using a packet sniffer). For the measurements, we use two examples that are included in the Wt distribution: hello (as an example of a minimal application), and composer (as an example of a simple, yet functional, application).

We propose to measure the time to create a new session, and the time of a small event.

Runtime: new session

Wt starts a new session by serving a small page to determines browser capabilities, and then triggers a second call to get the "main page", that has all visible content. To compare the relative performance for a particular platform, you should measure this "load" time, as the total duration of these two requests. You should measure the time from sending the first request, to sending the third request. The third request is either a GET request for auxiliary content (CSS or images), a GET request to a Wt resource, or a POST request to load invisible content in the background.

Runtime: event

We estimate the time needed to process a small event, such as a click on the "Greet me" button in hello, and "Save now" in composer, by measuring the total time for the packet exchange triggered by such an event.

Memory usage: basis

Measuring memory usage is a tricky thing, since code and read-only data memory used by shared libraries is effectively shared between processes, while writable data segments are obviously private to each process.

Therefore, we use pmap to study the memory in different segments. The basis RAM usage is divided between read-only segments, and writable segments. Only the latter are really constrained by physical RAM. We get the total writable size by summing the size of all writable segments, indicated by pmap with a w. The total size reported by pmap and top, minus the size of all writable segments is then the read-only RAM usage. Thus, this number includes shared libraries, and thus overestimates actual RAM usage.

Memory usage: per session

Compare the memory usage after starting 10 sessions with base memory usage, and divide the difference by 10 to estimate the memory used by a single session.

Platforms

ARM926EJ-S

Processor features

  • Clock-speed: 200 MHz
  • Linux BogoMIPS: 89.70
  • Caches: 8K instructions, 8K data

Configurations are ordered chronically, latest first.

Configuration 3: minimal (15/12/2010)

Setup
  • Wt version: Git (15/12/2010, > Wt 3.1.7)
  • Target system: Linux uclibc 2.6.23
  • Build environment: buildroot, arm-linux-gcc 4.2.1
  • Options: without multi-threading, libz and OpenSSL
  • Build type: full static build, except for: libstdc++, libc, and libm
  • Runtime settings: ./app.wt --docroot . --http-address 0.0.0.0 --no-compression
Performance results
Runtime-performance
Program New session (http) Event (http)
hello 0.19 s 0.06 s
composer 0.60 s 0.07 s

Configuration 2: minimal (16/03/2010)

Setup
  • Wt version: Git (16/03/2010, >= Wt 3.1.1)
  • Target system: Linux uclibc 2.6.23
  • Build environment: buildroot, arm-linux-gcc 4.2.1
  • Options: without multi-threading, libz and OpenSSL
  • Build type: full static build, except for: libstdc++, libc, and libm
  • Runtime settings: ./app.wt --docroot . --http-address 0.0.0.0 --no-compression
Performance results
Code size and RAM usage (in KBytes)
Program Code size (strip) Code size (strip + upx) RAM: basis † (read-only) RAM: basis (writable) RAM: per session
hello 1214 362 2544 228 14.8
composer 1462 420 2796 232 83.6

† includes shared libraries !

Runtime-performance
Program New session (http) Event (http)
hello 0.26 s 0.07 s
composer 0.69 s 0.08 s

Configuration 1: minimal (18/03/2008)

Setup - Original Test Page
  • Wt version: CVS-snapshot 18/03/08
  • Target system: Linux uclibc 2.6.23
  • Build environment: buildroot, arm-linux-gcc 4.2.1
  • Options: with multi-threading, but without libz and OpenSSL
  • Build type: full static build, except for: libc, libpthread, libdl, libstdc++, and libm
  • Build settings: MinSizeRel, -DHAVE_GNU_REGEX
  • Runtime settings: ./app.wt --docroot . --http-address 0.0.0.0 --threads=2 --no-compression
Performance results
Code size and RAM usage (in KBytes)
Program Code size (strip) Code size (strip + upx) RAM: basis † (read-only) RAM: basis (writable) RAM: per session
hello 1130 304 2580 372 28
composer 1265 332 2712 372 126

† includes shared libraries !

Runtime-performance
Program New session (http) Event (http)
hello 0.58 s 0.15 s
composer 1.8 s 0.15 s