powerpolicy.py 5.43 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
""" Power Policy Module:
    This module provides the interfaces that enable use of policies to control
    processor power using controls available in the processor.
    E.g. Dynamic Duty Cycle Modulation (DDCM), Dynamic Voltage
    and Frequency Scaling (DVFS) and Power capping

    The policies target problems like workload imbalance, memory saturation
    seen very often in parallel applications.

    To mitigate workload imbalance the policies adapt core frequencies to
    workload characteristics through use of core-specific power controls.
    The user can choose from three policies - DDCM, DVFS and a combination of
    DVFS and DDCM to mitiage  workload imbalance in parallel applications that
    use barrier synchronizations.
    The effective frequency of cpus not on the critical path of execution is
    reduced thereby lowering energy with little or no adverse impact on
    performance.

    Additional information:

    Bhalachandra, Sridutt, Allan Porterfield, Stephen L. Olivier, and Jan F.
    Prins. "An adaptive core-specific runtime for energy efficiency." In 2017
    IEEE International Parallel and Distributed Processing Symposium (IPDPS),
    pp. 947-956. 2017.

    Note: Power controls (DVFS, DDCM and power capping) needs to be enabled
    before using these interfaces. Please check your architecture specification
    for supported power contols and related information.
"""
import ddcmpolicy
31 32 33 34
import logging


logger = logging.getLogger('nrm')
35 36 37 38 39


class PowerPolicyManager:
    """ Used for power policy application """

40 41
    def __init__(self, cpus=None, policy=None, damper=1000000000,
                 slowdown=1.1):
42
        self.cpus = cpus
43 44 45 46 47 48 49 50 51 52 53
        self.policy = policy
        self.damper = damper
        self.slowdown = slowdown

        # Intiliaze all power interfaces
        self.ddcmpolicy = ddcmpolicy.DDCMPolicy()

        # Power levels
        self.maxdclevel = self.ddcmpolicy.maxdclevel
        # TODO: Need to set this value when DVFS policies are added
        self.maxfreqlevel = -1
54 55
        self.dclevel = dict.fromkeys(self.cpus, self.maxdclevel)
        self.freqlevel = dict.fromkeys(self.cpus, self.maxfreqlevel)
56 57 58 59

        # Book-keeping
        self.damperexits = 0
        self.slowdownexits = 0
60
        self.prevtolalphasetime = dict.fromkeys(self.cpus, None)
61

62
    def run_policy(self, phase_contexts):
63 64
        # Run only if policy is specified
        if self.policy:
65 66 67 68 69 70 71 72
            for id in phase_contexts:
                if id not in self.cpus:
                    logger.info("""Attempt to change power of cpu not in container
                                : %r""", id)
                    return
                # Select and invoke appropriate power policy
                # TODO: Need to add a better policy selection logic in addition
                # to user specified using manifest file
73 74 75 76 77 78 79 80
                ret, value = self.execute(id, **phase_contexts[id])
                if self.policy == 'DDCM':
                    if ret == 'DDCM':
                        self.dclevel[id] = value
                    # Incase of slowdown experienced by even process, reset all
                    # cpus
                    if ret == 'SLOWDOWN':
                        self.reset_all()
81 82
                phase_contexts[id]['set'] = False

83 84 85
    def execute(self, cpu, **kwargs):
        computetime = kwargs['computetime']
        totalphasetime = kwargs['totaltime']
86 87 88 89 90 91 92 93 94 95 96

        # If the current phase length is less than the damper value, then do
        # not use policy. This avoids use of policy during startup operation
        # insignificant phases
        if totalphasetime < self.damper:
            self.damperexits += 1
            return 'DAMPER', -1

        # If the current phase has slowed down beyond the threshold set, then
        # reset power. This helps correct error in policy application or acts
        # as a rudimentary way to detect phase change
97 98
        if(self.prevtolalphasetime[cpu] is not None and totalphasetime >
           self.slowdown * self.prevtolalphasetime[cpu]):
99 100 101
            self.ddcmpolicy.dc.reset(cpu)
            newdclevel = self.ddcmpolicy.maxdclevel

102 103 104
            # Reset value for next phase
            self.prevtolalphasetime[cpu] = totalphasetime

105 106 107
            return 'SLOWDOWN', newdclevel

        # Invoke the correct policy based on operation module
108 109 110
        if self.policy == "DDCM":
            newdclevel = self.ddcmpolicy.execute(cpu, self.dclevel[cpu],
                                                 computetime, totalphasetime)
111 112
            # Reset value for next phase
            self.prevtolalphasetime[cpu] = totalphasetime
113 114 115 116 117 118 119

        # TODO: Add DVFS and Combined policies

            return 'DDCM', newdclevel

    def print_policy_stats(self, resetflag=False):
        # Get statistics for policy run
120 121 122 123
        ppstats = dict()
        ppstats['PowerPolicyDamperExits'] = self.damperexits
        ppstats['PowerPolicySlowdownExits'] = self.slowdownexits
        ppstats.update(self.ddcmpolicy.print_stats(resetflag))
124 125 126 127
        if resetflag:
            self.damperexits = 0
            self.slowdownexits = 0

128 129
        return ppstats

130
    def power_reset(self, cpu):
131
        # Reset power control
132 133
        self.ddcmpolicy.dc.reset(cpu)

134
        # Reset value
135 136 137 138 139
        self.dclevel[cpu] = self.maxdclevel

    def power_check(self, cpu):
        # Check status of all power controls
        return self.ddcmpolicy.dc.check(cpu)
140 141 142 143 144

    def reset_all(self):
        # Reset all cpus
        for cpu in self.cpus:
            self.power_reset(cpu)