Harlequin RIP SDK
Configuring memory

All shipping versions of the Harlequin RIP use the same core, including the same memory manager.

This section describes how the Harlequin memory manager can be configured for the Harlequin RIP.

The memory pool system

The Harlequin RIP uses the Memory Pool System (MPS) internally (https://www.ravenbrook.com/project/mps). The Memory Pool System was originally developed by Harlequin Ltd., but is now an open-source project supported by Ravenbrook Ltd.

MPS is a fast allocation, flexible memory management system, supporting allocation and release of memory to and from the operating system. It includes garbage collection and customizable memory pool types. The integration of MPS in the Harlequin RIP is a custom version built to support RIP allocation patterns and requirements.

The memory arena is a fundamental concept in the MPS. Memory arenas control how the MPS acquires and releases memory segments from the operating system.

Virtual address spaces and physical memory

The Harlequin RIP runs on a range of operating systems. Operating systems used for server and desktop machines support virtual memory, which provides automatic mapping of a large address space to the physical memory and paging of memory to disk.

The Harlequin RIP can run on systems with or without virtual memory. The MPS can use the larger address space available with a virtual memory system to operate more efficiently.

Harlequin RIP memory models

The Harlequin RIP core library used by Harlequin Core may be configured to interact with the operating system in two different ways:

  • The MPS may be configured to sub-allocate memory for the RIP from a single, fixed block of memory provided by your integration. This method is generally appropriate for embedded systems where virtual memory is not present; where the RIP is one of a set of known processes running on the device; and for applications where having a fixed amount of physical memory dedicated to just the RIP is an advantage.
  • The MPS may be configured to reserve a range of address space, and then map and unmap physical memory pages from the operating system into that address space. This method is generally appropriate for server and desktop operating systems that support virtual memory, and for integrations that use multiple RIP or other workflow processes on the same machine. The RIP allocates memory through the MPS; the MPS places the RIP's allocations into pools, consisting of a number of memory segments. The MPS maps physical memory pages into the virtual address space as the RIP's memory demands increase, and it also releases unused segments back to the operating system as the memory demands of the RIP reduce.

While it is theoretically possible to create a new MPS arena type to implement interactions with the operating system other than those above, Global Graphics does not recommend this. If a behavior other than the two methods above is required, you should contact Global Graphics about your requirements.

Memory initialization APIs

SwLeDo() is the preferred method for controlling the Harlequin SDK and RIP. The calls to SwLeSDKStart(), SwLeMemInit() and SwLeStart() described in this and the next section will all be made in the correct sequence when using SwLeDo().

The Harlequin RIP SDK provides higher-level APIs to configure the RIP than are supplied by the core library. At an early point in the application initialization, the SDK function SwLeSDKStart() will be called, either directly in older integrations or from SwLeDo() in newer integrations. The SwLeSDKStart() call initializes the MPS first and then several other support libraries used by the RIP. The RIP SDK initialization can be separated from RIP initialization because several of the libraries and components are useful outside of the scope of the RIP, and also provide information that the RIP may require during initialization.

The parameters to SwLeSDKStart() are used to provide the sizes of the address space and working memory requested (and the memory block if using a fixed allocation), and also to return the actual sizes of the address space and working size reserved for the RIP.

SwLeMemInit(), SwLeStart(), and SwLeDo().

SwLeDo() is the preferred method for controlling the Harlequin SDK and RIP. SwLeDo() can perform multiple initialization and processing steps in one call, including calling the SwLeSDKStart(), SwLeStart() and SwLeMemInit() functions. When using SwLeDo(), the memory parameters are passed through the SwLeDoParams structure:

The memory parameters should be initialized as desired in the SwLeDoParams structure before the first call to SwLeDo(), and then not modified until after the SDK is shut down.

As well as SwLeSDKStart(), there are two other API functions that require parameters to configure the RIP's memory behavior. SwLeMemInit() and SwLeStart() are both passed the same maximum address space, maximum working size, and memory block parameters set by or provided to SwLeSDKStart().

SwLeStart() is the function that starts the RIP, and so it will always be called. The SwLeMemInit() function is optional. The first of these two functions to be called sets the memory parameters that will be used by the RIP; if SwLeMemInit() is called, the memory-related parameters to the SwLeStart() call are ignored.

Both SwLeStart() and SwLeMemInit() also take a RIP_emergencySizeInBytes parameter. This parameter is only relevant when the RIP is configured to use virtual memory. The amount of physical memory the RIP is allowed to use (RIP_workingSizeInBytes) is divided into two parts, by subtracting the emergency size from the supplied working size. The RIP will allocate memory up to this new lower working size without penalty. Upon reaching this limit, the RIP will run through its low-memory strategies (such as flushing caches, paging data to disk if possible).

Memory allocations are allowed to extend the total allocated memory into the emergency memory size as one of the final low-memory actions. The RIP will continue to perform low-memory actions until the amount of allocated memory falls below the new lower working size, and the RIP can release memory back to the operating system. Performance of the RIP will be negatively affected while it is using more than the lower working set size.

The emergency size should be set to a small fraction of the RIP_workingSizeInBytes parameter. The SDK normally defaults it to a fixed 5 MB allocation.

The Harlequin RIP on Windows

The previous sections describe the behavior of the RIP in abstract terms. On Windows, the two memory models described translate into behavior in this manner:

  • When configuring the RIP with a single memory block allocation, the SDK example RIP wrappers use VirtualAlloc(NULL, size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE) to allocate the memory. All of the pages for the allocation are committed immediately. Windows does not allow more than the RAM size (physical plus swap size) to be committed at any time, so this guarantees that the RIP will be able to sub-allocate exactly this amount of memory. On the other hand, this also fixes this amount of memory so that other processes cannot use it, even when the RIP does not need the memory.
  • When configuring the RIP to use virtual memory, the main part of the RIP's address space is initially reserved using VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS) This reserves address space, but does not commit physical pages to those addresses. When MPS needs to get new segments to fulfill allocations, it will use VirtualAlloc(base, addr, MEM_COMMIT, PAGE_EXECUTE_READWRITE) to commit a physical page. When MPS can release segments, it uses VirtualFree(base, addr, MEM_DECOMMIT) to return the segments to the operating system. There is some hysteresis in returning segments to the operating system. Normally, MPS retains 256 KB of space, so it does not immediately need to re-map data from the operating system after unmapping data. The amount of hysteresis is configurable, but this is normally not necessary.

Memory accounting on Windows is complex. There is no single easy measure of the amount of physical memory that a process uses, because of shared objects, paging, and on-demand initialization of pages from the virtual memory system. The Private Bytes measure is possibly the best indication of the amount of physical memory the RIP has committed. Process Explorer can be configured to show this figure, as well as the amount of address space requested by the RIP (the Virtual Size). Windows Task Manager shows the Private Working Set as its memory figure, which is the amount of memory actually in-use by the RIP process.

Determining memory allocation parameters

Depending on your expectations for throughput or latency, and whether you are running entire jobs as a single run or, for example, splitting pages to a different RIP, there is a requirement to determine how much memory to reserve for other (non-RIP) processes, how many RIPs you will run on a machine, and whether swapping is ever acceptable.

If swapping is never acceptable, you should subtract the reserve for other processes from the physical memory total, and divide that by the number of RIPs to run. If that number is too small to allow jobs through, you should either dynamically control the number of RIP processes running and their working size allocations, or accept that on occasions swapping may happen and allow more total working size than the machine has physical memory.

Swapping allows a computer to execute programs and manipulate data files larger than main memory.