High Performance

Nano aims to promote high-performance development, both at run-time and during the development process.

It does this by making it easy to write simple, yet efficient, code.

Simpler Code

Nano aims to minimise and simplify the code needed for common tasks.

This example compares some simple string manipulation with the equivalent Objective-C:

void CObject::SomeMethod(const NString &theString) { NString otherString; otherString = theString; otherString += " World!"; if (theString == "Hello") CFShowStr(ToCF(otherString)); }
- (void) someMessage:(const NSString *) theString { NSMutableString *otherString = nil; otherString = [theString mutableCopyWithZone:nil]; [otherString appendString:@" World!"]; if ([theString isEqualToString:@"Hello"]) CFShowStr((CFStringRef) otherString); [otherString release]; }

As well as being less verbose, notable differences are:

  • Since Cocoa has two string classes, each variable must be explicitly declared as immutable or mutable. Nano provides a single NString object, which becomes mutable as needed (allowing const to enforce const-ness at compile-time).

  • Although it is overwritten in this example, local variables such as otherString must be explicitly initialized in Objective-C. Nano objects are always initialised to a sensible default (such as an empty string).

  • NString provides '=' and '+=' operators, allowing a more natural syntax for string manipulation.

  • Objects derived from NComparable, such as NString, only need to implement one method to obtain the full set of comparison operators (==, !=, <=, <, >, >=).

  • Objects derived from NSharedValue, such as NString, use a copy-on-write model for data. This allows otherString to be assigned theString, yet defer copying theString's data until otherString is modified. In Objective-C, otherString must duplicate theString if it could potentially be modified (even if that modification is inside a branch which never occurs).

  • A set of 'Nanite' headers provide overloaded functions to convert between Nano types and other APIs. Rather than having to explicitly specify the type being cast to, ToCF automatically invokes the appropriate function to convert a Nano object to the equivalent CoreFoundation object.

  • Nano uses the 'RAII' design pattern to minimise pointer management, allowing otherString to release its string when it goes out of scope. This must be managed manually in Objective-C, or handled with garbage collection (which can reclaim pointers, but not resources such as open files) or ARC (which can require special casts to inform the compiler about object lifetimes).

Simpler Multithreading

Nano provides a cross-platform interface to the native thread API, and a set of synchronization primitives such as locks (mutex, read-write, and spin) and atomic operations (integers, lists, or arithmetic).

NThreadPool manages a thread pool for NThreadTask objects, and automatically distributes tasks across the available CPUs. Tasks are normally scheduled by priority, but NThreadPool can also use FIFO, LIFO, or custom schedulers.

Individual functors can be spun off onto threads, and threads can pass functors back to the main thread if they need to access to non-thread-safe APIs.

Simpler Sockets

Nano provides a cross-platform interface to the native sockets API, and a set of tools for building network applications (such as a key-value based message system, or ZeroConf discovery).

NSocket is a thin wrapper around the most efficient network layer for each platform, and abstracts the differences in behaviour between notify-on-ready (CFSocket) and notify-on-completion (IOCP) APIs.

NMessageClient and NMessageServer allow multiple clients to connect to a server, and communicate using compressed key-value messages (with comparable performance to Protocol Buffers).

Copy-On-Write

Many of Nano's core types derive from NSharedValue, which provides lock-free copy-on-write behaviour. This allows objects such as strings or dictionaries to be copied extremely efficiently.

NSharedValue allows objects to be passed around with very little overhead, as even objects passed by value are effectively passed as const references until they are actually modified.

This approach also allows Nano to provide a single object of each type (e.g., NString), rather than two classes for the same concept (such as separate classes for mutable vs immutable strings).

Immutable objects are "copied" simply by incrementing their reference count, and are converted to a mutable form automatically as required.

Inline Functions

By judicious use of inline functions, Nano allows performance-critical code to preserve type safety while removing any function call overhead:

NIndex CountBits(const NBitVector &theBits) { NIndex n, numBits, numSet; numBits = theBits.GetSize(); numSet = 0; for (n = 0; n < numBits; n++) { if (theBits.GetBit(n)) numSet++; } return(numSet); }

Since GetBit is an inline function, the inner loop can obtain direct access to the bits in the vector without going through a real function call.

However since GetBits is declared as a function, it can still ensure const-correctness and be stepped into for debugging.