Skip to content

Runtime performance considerations

Keep event-thread work short, queues bounded and ownership explicit. Emulator speed depends on its CPU backend and host; measure on the target device before using emulator timings to choose a production configuration.

Allocation and image size

The guest runtime defaults to mimalloc over process-owned RChunk pages. It reserves an 8 MiB virtual arena and caps total chunk address space at 64 MiB by default; SYMBIAN_MIMALLOC_ADDRESS_BUDGET_MIB accepts 16–512 MiB. Reserved address space and committed physical memory are different costs. The alternative SYMBIAN_RUNTIME_MIMALLOC=OFF profile uses the Symbian heap.

Ordinary allocation failure terminates with KErrNoMemory; nothrow allocation returns null. Bound input and buffer sizes before constructing containers. Reuse buffers for file copying, network bodies and other repeated work. Link only required components: final image size depends on reachable functions, imports and runtime profile, rather than static archive size alone.

Choose an execution model

Mechanism Cost to consider Application guidance
Inline Future continuation Work runs on the registering or completing thread Keep callbacks short; marshal UI changes to the event owner
Worker continuation Queue, result transfer and OS-thread wakeup Use for blocking I/O and computation
Fiber Stack memory and cooperative switches Size the stack for the workload; blocking calls still block its OS thread
Native timer OS handle and Promise state per pending timer Reuse the event loop; keep an admission limit
Property watch Handle and Future state per outstanding subscription Rearm deliberately and drain cancellation before close
Event mailbox Callback storage and dispatch turns Bound admission and the work performed in one turn

TimerPump admits 64 pending timers by default and EventMailbox admits 128 callbacks. These limits do not bound completed Futures retained by application code or arbitrary continuation fan-out. Tune them for memory and responsiveness. A worker fiber defaults to a 16 KiB stack; TLS handshakes can require a larger stack. See the concurrency guide.

Copy a file without loading it all

FileCopy reuses a 32 KiB buffer and exposes progress after each transfer. This helper creates a new destination so that an existing backup is not overwritten:

#include <functional>

#include "symbian/api/storage/storage.h"

absl::Status CopyBackup(
    std::u16string_view source, std::u16string_view destination,
    const std::function<void(symbian::api::storage::CopyProgress)>& progress) {
  namespace files = symbian::api::storage;
  auto copy =
      files::FileCopy::Open(source, destination, files::WriteMode::kCreateNew);
  if (!copy.ok()) {
    return copy.status();
  }
  for (;;) {
    auto step = copy->Step();
    if (!step.ok()) {
      return step.status();
    }
    if (progress) {
      progress(*step);
    }
    if (step->complete) {
      return absl::OkStatus();
    }
  }
}

Link Symbian::Storage, create the destination's parent directory first, and run the whole helper on one worker. Its callback runs there too: post progress to the event thread rather than touching a window directly. A failure can leave a partial destination; choose a cleanup policy for your application. For cancellable jobs, retain FileCopy in the worker's job state and request Cancel() while that state remains alive.

Clocks, atomics and formatting

Use absl::Time and absl::Duration for deadlines. For short measurements, check the reported frequency, direction and power cost before using a native fast counter. The SDK steady clock uses a native tick source; its resolution depends on the device.

Default 64-bit atomics serialize through a process-wide RFastLock. The optional native atomic profile requires matching EUSER exports and can have different contention costs. Use the standard runtime is_lock_free() query rather than assuming an ARMv6 compiler target makes the linked implementation lock-free.

String streams, error messages and status payloads can allocate. Keep formatting and diagnostics out of hot redraw or sample-processing loops when possible. When comparing implementations, measure cold startup separately from warmed runs and include peak memory, allocation failures, cancellation and UI latency.