Read the original article:Performance Tips and Techniques for Huawei Lite Wearable Devices
Introduction
Optimizing performance for constrained environments - especially lite wearables with slow CPU, and limited memory - requires a different mindset. While the techniques in this article are especially relevant for devices with strict hardware limitations, they apply broadly to many performance-sensitive contexts.
This is not a list of rules to memorize, but ideas to internalize. Each item could be an article on its own. Focus on understanding the principles so you can adapt them to your runtime and device.
Core Mentality: Do less, use less.
- Reduce CPU cycles: fewer computations, simpler algorithms, cached results
- Minimize memory allocations: object pooling, pre-allocation, efficient data structures
- Prevent garbage collector pressure: avoid creating temporary objects, reuse resources
- Limit I/O operations: batch operations, reduce file system calls, optimize network requests
Learn the Language Characteristics
JS Functional Programming Trade-offs
- Functional programming principles are good. Immutability, pure functions, and composability create maintainable code.
- Performance of FP in JavaScript suffers with limited resources - the abstraction is expensive.
- FP in JS often incurs unnecessary memory allocation: eg: .map(), .filter(), and .reduce() creates intermediate arrays.
- It creates garbage collector pressure: frequent allocations trigger GC cycles.
- So just use imperative loops for hot paths / slow parts.
String Optimization
- In JavaScript, strings are immutable - every operation like slice() or trim() creates a new string in memory.
- Concatenation str1 + str2 + str3 creates temporary strings at each step.
- Better alternatives: Work with character codes directly for parsing operations.
- String interning: Reuse frequently accessed strings instead of recreating them.
Prefer Binary Data Handling
- ArrayBuffer (eg: uint8array) are mutable and performant - direct memory manipulation without allocations.
- Prefer when possible - most standard library functions support them (eg: FileIO). Sometimes using them is the only way. (eg: HUKS)
- Practical applications: Hash functions, encryption, decryption, file I/O.
Serialization Strategy
- JSON.parse and JSON.stringify are expensive - they're optimized for readability and flexibility, not for performance. Avoid them if you process big amount of data or a lot of small data.
- For large datasets: Avoid JSON entirely.
- Find alternative solutions, write your own serializer - deserializer depending on the shape of your data.
Create Specific Solutions for Specific Problems
Generic solutions work, but specific problems deserve specific optimizations. Understand the problem before reaching for general algorithms.
Examples
CASE
In a 2D game, two guns with constant Y coordinate fire bullets. If bullets from opposite guns collide, they’re destroyed. Check collusions and destroy them.
BAD
- For g1b of gun1.bullets
- For g2b of gun2.bullets
- If collide(g1b, g2b) { destroy(g1b); destroy(g2b) }
GOOD
- var g1ob = gun1.oldest_bullet
- var g2ob = gun2.oldest_bullet
- If !nil(g1ob) and !nil(g2ob) and collide(g1ob, g2ob) { destroy(g1ob); destroy(g2ob) }
In this specific case, bullets travel directly toward each other. So only the oldest bullets from each gun can possibly collide. All other combinations are impossible due to timing and direction.
This reduces complexity from O(n²) to O(1) and eliminates unnecessary CPU and memory overhead.
CASE
Lite wearable devices have no RDB, no SQLite. Where to put data?
BAD:
- Put it into Key Value Store.
- Key Value Store has limitations. Max value length is 128 bytes.
- What can we do? Chunk your data, store in some specific keys, maybe suffixing with increasing numbers.
- To read: Send tons of read requests to the Key Value Store, concat all the returning text, parse it and use.
GOOD
- Convert your string to a Uint8Array. Write to file in a single call.
- To read: Read whole file via single call as string.
CASE
Find a specific delimiter data coming from wearengine file. Split it as [part1, part2]. Decrypt part 2 by using part 1 as the key. Store decrypted content in a new file.
BAD
- Read wearengine file as string.
- Run myString.split(“delimiter”).
- Convert both parts to array buffers, so Huks will accept it as a parameter.
- Decrypt it.
- Convert to a string.
- Write to a file by using fs write string. (Max 4KB, so chunk and append to the same file.)
GOOD
- Read wearengine file as array buffer.
- Find the byte sequence of delimiter in the byte array.
- Use subarray if available in the runtime. If not, create 2 new byte arrays.
- Give them to HUKS.
- Write to the file by using fs write array buffer.
CASE
Process 100 sensor readings per second and detect anomalies by analyzing last 10 seconds of data.
BAD
- Create new objects for each reading: readings.push({value: x, timestamp: Date.now()})
- Use array methods: readings.filter(r => r.value > threshold).map(r => r.value)
- Allocate new arrays for each filtering operation
- Recalculate statistics from scratch each time: array.reduce((a,b) => a + b)
GOOD
- Pre-allocate circular buffer: const buffer = new Float32Array(1000)
- Use index pointer: buffer[index++ % 1000] = newValue
- Recalculate statistics incrementally (sum = sum - oldValue + newValue)
Conclusion
A significant part of software development is about making trade-offs. This article doesn’t aim to take sides in broader debates around programming paradigms or best practices.
Instead, it focuses on scenarios where performance becomes a bottleneck - and in those cases, revisiting your current choices and applying some of the techniques discussed here might be the trade-off you need to make.
These aren't replacements for good architecture or idiomatic code. They're tools - to be used selectively, with purpose, when the context demands it.
References
https://device.harmonyos.com/en/docs/apiref/doc-guides/lite-wearable-overview-0000001197577411
Top comments (0)