Since each node's power level can be separately controlled, total memory power can be reduced by selectively setting nodes to operate at lower power levels. However, selecting which nodes to put into lower power modes is critical to both system performance and power dissipation, since accessing a node in a low-power mode will incur resynchronization costs, stall execution, and, as a result, may increase energy consumption, offsetting any prior savings.
To avoid such costs, we need to ensure that all the nodes a process may
access, i.e., its active nodes, are kept in high-power state.
More specifically, we define a node to be an active node of process
if and only if at least one page from the node is mapped into process
's address space, and we denote the set of active nodes for
as
.
By promoting the nodes in
to Standby mode (high-power) and
demoting all other nodes (i.e., those in
) to Nap
mode (low-power) when process
is executing, we can reduce power
while ensuring that process
suffers no performance degradation due
to the increased latency of nodes in low-power states.
Of course, this assumes that can be managed to accurately
reflect the active nodes for process
. Previous related
research [4] used repeated page faults and page table
scans to track the active set, but this involves very expensive, high
overhead operations. To track the active set with minimal
overheads, for each process we keep an array of counters, each of which
is associated with a node in the system. The kernel is modified such
that on all possible execution paths in which a page is allocated for,
or mapped into, process
's address space, the counter associated with
the node containing this page is incremented. Similarly, when a page is
unmapped, the counter is decremented. From these counters,
is trivially derived: a node is in
if and only if process
's counter for the node is greater than zero. The overhead of
maintaining
is only one extra instruction per mapping/unmapping
operation, and is therefore negligible.