retarget.py 3.92 KB
Newer Older
dugupeiwen's avatar
dugupeiwen committed
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""
Implement utils for supporting retargeting of dispatchers.

WARNING: Features defined in this file are experimental. The API may change
         without notice.
"""
import abc
import weakref

from numba.core import errors


class RetargetCache:
    """Cache for retargeted dispatchers.

    The cache uses the original dispatcher as the key.
    """
    container_type = weakref.WeakKeyDictionary

    def __init__(self):
        self._cache = self.container_type()
        self._stat_hit = 0
        self._stat_miss = 0

    def save_cache(self, orig_disp, new_disp):
        """Save a dispatcher associated with the given key.
        """
        self._cache[orig_disp] = new_disp

    def load_cache(self, orig_disp):
        """Load a dispatcher associated with the given key.
        """
        out = self._cache.get(orig_disp)
        if out is None:
            self._stat_miss += 1
        else:
            self._stat_hit += 1
        return out

    def items(self):
        """Returns the contents of the cache.
        """
        return self._cache.items()

    def stats(self):
        """Returns stats regarding cache hit/miss.
        """
        return {'hit': self._stat_hit, 'miss': self._stat_miss}


class BaseRetarget(abc.ABC):
    """Abstract base class for retargeting logic.
    """
    @abc.abstractmethod
    def check_compatible(self, orig_disp):
        """Check that the retarget is compatible.

        This method does not return anything meaningful (e.g. None)
        Incompatibility is signalled via raising an exception.
        """
        pass

    @abc.abstractmethod
    def retarget(self, orig_disp):
        """Retargets the given dispatcher and returns a new dispatcher-like
        callable. Or, returns the original dispatcher if the the target_backend
        will not change.
        """
        pass


class BasicRetarget(BaseRetarget):
    """A basic retargeting implementation for a single output target.

    This class has two abstract methods/properties that subclasses must define.

    - `output_target` must return output target name.
    - `compile_retarget` must define the logic to retarget the given dispatcher.

    By default, this class uses `RetargetCache` as the internal cache. This
    can be modified by overriding the `.cache_type` class attribute.

    """
    cache_type = RetargetCache

    def __init__(self):
        self.cache = self.cache_type()

    @abc.abstractproperty
    def output_target(self) -> str:
        """Returns the output target name.

        See numba/tests/test_retargeting.py for example usage.
        """
        pass

    @abc.abstractmethod
    def compile_retarget(self, orig_disp):
        """Returns the retargeted dispatcher.

        See numba/tests/test_retargeting.py for example usage.
        """
        pass

    def check_compatible(self, orig_disp):
        """
        This implementation checks that
        `self.output_target == orig_disp._required_target_backend`
        """
        required_target = orig_disp._required_target_backend
        output_target = self.output_target
        if required_target is not None:
            if output_target != required_target:
                m = ("The output target does match the required target: "
                     f"{output_target} != {required_target}.")
                raise errors.CompilerError(m)

    def retarget(self, orig_disp):
        """Apply retargeting to orig_disp.

        The retargeted dispatchers are cached for future use.
        """
        cache = self.cache
        opts = orig_disp.targetoptions
        # Skip if the original dispatcher is targeting the same output target
        if opts.get('target_backend') == self.output_target:
            return orig_disp
        cached = cache.load_cache(orig_disp)
        # No cache?
        if cached is None:
            out = self.compile_retarget(orig_disp)
            cache.save_cache(orig_disp, out)
        else:
            out = cached
        return out