Esempio n. 1
0
def tt_predecessor(g, u, v, w, k):
    tokens = g.get_edge_data(u, v).get('tokens', 0)
    prates = g.get_edge_data(v, w).get('production', cyclic(1))
    crates = g.get_edge_data(u, v).get('consumption', cyclic(1))
    # determine psi
    phi = g.node[v].get('phases')
    minval = None
    for i in range(phi):
        value = (k - sum(prates[:i]) + prates.sum() - 1) // prates.sum()
        value = value * phi + i
        minval = value if minval is None else min(value, minval)

    result1 = sum(crates[:minval]) - tokens

    minval = None
    for i in range(phi):
        value = (k - sum(prates[:i]) + prates.sum() - 1) // prates.sum()
        value = value * crates.sum() + sum(crates[:i]) - tokens
        minval = value if minval is None else min(value, minval)

    result2 = minval

    maxval = None
    for i in range(phi):
        value = (k - 1 - sum(prates[:i])) // prates.sum()
        value = value * crates.sum() + sum(crates[:i + 1]) - tokens
        maxval = value if maxval is None else max(value, maxval)

    result3 = maxval

    return result1, result2, result3
Esempio n. 2
0
def lin_bounds(g, v, w):
    tokens = g.get_edge_data(v, w).get('tokens', 0)
    prates = g.get_edge_data(v, w).get('production', cyclic(1))
    crates = g.get_edge_data(v, w).get('consumption', cyclic(1))
    phi_v = g.node[v].get('phases')
    phi_w = g.node[w].get('phases')
    g_vw = gcd(prates.sum(), crates.sum())
    qv = g.q[v]
    qw = g.q[w]
    minval = None
    maxval = None

    for i in range(phi_v):
        for j in range(phi_w):
            value = Fraction(
                g_vw * phi_v *
                (tokens + sum(prates[:i]) - sum(crates[:j])) // g_vw,
                prates.sum()) - i + j * Fraction(qv, qw)
            maxval = value if maxval is None else max(value, maxval)
            value = Fraction(
                g_vw * phi_v *
                (tokens + g_vw + sum(prates[:i]) - sum(crates[:j])) // g_vw,
                prates.sum()) - i + j * Fraction(qv, qw)
            minval = value if minval is None else min(value, minval)

    return minval - 1, maxval
Esempio n. 3
0
def analyse_cycle(g, cycle):
    maxgain, arg = Fraction(0, 1), None
    for idx in range(len(cycle)):
        data = g.get_edge_data(* cycle[idx] )
        pr = data.get('production', core.cyclic(1))
        cr = data.get('consumption', core.cyclic(1))
        toks = data.get('tokens', 0)
        gain = Fraction(pr.sum(), cr.sum())
        if gain > maxgain:
            maxgain = gain
            arg = idx

    data = g.get_edge_data(* cycle[arg] )
    pr = data.get('production', core.cyclic(1))
    cr = data.get('consumption', core.cyclic(1))
    toks = data.get('tokens', 0)

    if len(pr) == len(cr) == 1:
        _gcd = gcd(pr.sum(), cr.sum())
        pr = pr.sum() // _gcd
        cr = cr.sum() // _gcd
        toks = toks // _gcd
        # solve  toks + i * pr = 0 (mod cr)
        i = (-toks * imath.modinv( pr, cr)) % cr
        return find_cycle(g, cycle, arg, i)

    return find_cycle(g, cycle, 0)
Esempio n. 4
0
def tt_predecessor( g, u, v, w, k ):
    tokens = g.get_edge_data(u, v).get('tokens', 0)
    prates = g.get_edge_data(v, w).get('production', cyclic(1))
    crates = g.get_edge_data(u, v).get('consumption', cyclic(1))
    # determine psi
    phi = g.node[v].get('phases')
    minval = None
    for i in range( phi ):
        value = (k - sum(prates[:i]) + prates.sum() - 1) // prates.sum()
        value = value * phi + i
        minval = value if minval is None else min(value, minval)

    result1 = sum(crates[:minval]) - tokens

    minval = None
    for i in range( phi ):
        value = (k - sum(prates[:i]) + prates.sum() - 1) // prates.sum()
        value = value * crates.sum() + sum(crates[:i]) - tokens
        minval = value if minval is None else min(value, minval)

    result2 = minval

    maxval = None
    for i in range( phi ):
        value = (k - 1 - sum(prates[:i])) // prates.sum()
        value = value * crates.sum() + sum(crates[:i + 1]) - tokens
        maxval = value if maxval is None else max(value, maxval)

    result3 = maxval

    return result1, result2, result3
Esempio n. 5
0
def predecessor_lin_bounds(moduli=dict(), bindings=dict(), **kwargs):
    prates = kwargs.get('production', cyclic(1))
    crates = kwargs.get('consumption', cyclic(1))
    tokens = kwargs.get('tokens', 0)
    g = gcd(prates.sum(), crates.sum())
    avg_prate = Fraction(prates.sum(), len(prates))
    avg_crate = Fraction(crates.sum(), len(crates))
    minval = None
    maxval = None
    delta = tokens
    if 'var' in kwargs:
        varname = kwargs['var']
        if varname in bindings:
            delta = delta + bindings[varname]
        else:
            _, modulus = moduli.get(varname, (1, 0))
            delta = delta + modulus

    for i in range(len(prates)):
        for j in range(len(crates)):
            val_ij = g * (delta // g) - i * avg_prate + j * avg_crate

            minval = val_ij if minval is None else min(minval, val_ij)
            maxval = val_ij if maxval is None else max(maxval, val_ij)
            delta -= crates[j]
        delta = delta + prates[i] + crates.sum()

    return maxval, minval + g - avg_prate
Esempio n. 6
0
def single_rate_equivalent(sdfg):
    if not sdfg.is_consistent():
        raise ValueError('Inconsistent SDF graph')

    hsdfg = nx.DiGraph()
    for v in sdfg.q:
        assert 'wcet' in sdfg.node[
            v], "Missing 'wcet' attribute for node {}".format(v)
        wcets = cyclic(sdfg.node[v]['wcet'])

        for i in range(sdfg.q[v]):
            hsdfg.add_node((v, i + 1), wcet=wcets[i % len(wcets)])

        for u, _, data in sdfg.in_edges_iter(v, True):
            prates = data.get('production', cyclic(1))
            crates = data.get('consumption', cyclic(1))
            tokens = data.get('tokens', 0)
            for j in range(sdfg.q[v]):
                extra_data = dict()
                i = predecessor(j + 1, **data)
                hsdfg.add_edge((u, (i - 1) % sdfg.q[u] + 1), (v, j + 1),
                               tokens=(sdfg.q[u] - i) // sdfg.q[u],
                               **extra_data)

    assert hsdfg.number_of_nodes() == sum(sdfg.q.values(
    )), "Size of HSDF graph is equal to sum of repetition vector"
    return hsdfg
Esempio n. 7
0
def parallelise(g, vectors = None, **kwargs):
    vectors = vectors or kwargs
    result = core.SDFGraph()
    for v, data in g.nodes_iter( data = True ):
        parallelism = core.cyclic(vectors.get(v, [1]))
        wcet = data.get('wcet')
        assert len(parallelism) > 0, "Invalid parallelism for {}: {} (vectors: {})".format(v, parallelism, vectors)
        result.add_node( v, wcet = wcet * len( parallelism ))

    for v, w, data in g.edges_iter( data = True ):
        prates = data.get('production', core.cyclic(1))
        crates = data.get('consumption', core.cyclic(1))
        tokens = data.get('tokens', 0)

        parallelism_v = core.cyclic(vectors.get(v, 1))
        parallelism_w = core.cyclic(vectors.get(w, 1))

        s = 0
        new_prates = list()
        for i in range( (len( prates ) * len( parallelism_v )) // gcd( len( prates ), sum( parallelism_v ))):
            new_prates.append( prates.sum( s, s + parallelism_v[ i ] ))
            s = s + parallelism_v[ i ]

        s = 0
        new_crates = list()
        for i in range( (len( crates ) * len( parallelism_w )) // gcd( len( crates ), sum( parallelism_w ))):
            new_crates.append( crates.sum( s, s + parallelism_w[ i ] ))
            s = s + parallelism_w[ i ]

        result.add_edge( v, w,
            production = new_prates,
            consumption = new_crates,
            tokens = tokens )

    return result
Esempio n. 8
0
def predecessor_lin_bounds(moduli = dict(), bindings = dict(), **kwargs):
    prates = kwargs.get('production', cyclic(1))
    crates = kwargs.get('consumption', cyclic(1))
    tokens = kwargs.get('tokens', 0)
    g = gcd(prates.sum(), crates.sum())
    avg_prate = Fraction( prates.sum(), len(prates) )
    avg_crate = Fraction( crates.sum(), len(crates) )
    minval = None
    maxval = None
    delta = tokens
    if 'var' in kwargs:
        varname = kwargs['var']
        if varname in bindings:
            delta = delta + bindings[ varname ]
        else:
            _, modulus = moduli.get(varname, (1, 0))
            delta = delta + modulus

    for i in range(len(prates)):
        for j in range(len(crates)):
            val_ij = g * (delta // g) - i * avg_prate + j * avg_crate

            minval = val_ij if minval is None else min( minval, val_ij )
            maxval = val_ij if maxval is None else max( maxval, val_ij )
            delta -= crates[j]
        delta = delta + prates[i] + crates.sum()

    return maxval, minval + g - avg_prate
Esempio n. 9
0
def channel_tokens( np, nc, sdfg = None, edge = None, **kwargs ):
    """
    Computes the number of tokens that are on the specified channel after np productions
    and nc consumptions.
    """
    data = sdfg.get_edge_data(*edge) if sdfg is not None else kwargs
    prates = data.get('production', cyclic(1))
    crates = data.get('consumption', cyclic(1))
    tokens = data.get('tokens', 0)

    return tokens + sum(prates[:np]) - sum(crates[:nc])
Esempio n. 10
0
def channel_tokens(np, nc, sdfg=None, edge=None, **kwargs):
    """
    Computes the number of tokens that are on the specified channel after np productions
    and nc consumptions.
    """
    data = sdfg.get_edge_data(*edge) if sdfg is not None else kwargs
    prates = data.get('production', cyclic(1))
    crates = data.get('consumption', cyclic(1))
    tokens = data.get('tokens', 0)

    return tokens + sum(prates[:np]) - sum(crates[:nc])
Esempio n. 11
0
def multi_rate_equivalent(sdfg):
    result = core.SDFGraph()
    for v, data in sdfg.nodes_iter(data=True):
        phi = data.get('phases', 1)
        for i in range(phi):
            result.add_node((v, i + 1),
                            wcet=data.get('wcet', core.cyclic(0))[i])

    for v, w, data in sdfg.edges_iter(data=True):
        Tv = sdfg.node[v].get('phases', 1)
        Tw = sdfg.node[w].get('phases', 1)
        prates = data.get('production', core.cyclic(1))
        crates = data.get('consumption', core.cyclic(1))
        tokens = data.get('tokens', 0)
        plen = len(prates)
        assert Tv == len(prates)
        assert Tw == len(crates)

        # consuming actor in unfolded graph has a single phase
        # and consumption rate equal to crates.sum()
        csum = crates.sum()

        # sum of production rates in multi-rate equivalent of unfolded graph
        psum = prates.sum()

        gvw = gcd(csum, psum)

        # iterate consuming actors
        for j in range(Tw):
            # determine which incoming channels must be added
            # see Algorithm ? in PhD thesis
            for i in range(plen):
                delta_i_n = tokens + prates.sum(stop=i +
                                                1) - crates.sum(stop=j + 1)
                delta_i1_n = tokens + prates.sum(stop=i) - crates.sum(stop=j +
                                                                      1)
                sol_min = (gvw - delta_i_n - 1) // gvw
                sol_max = (gvw - delta_i1_n - 1) // gvw

                if sol_min < sol_max:
                    # add channel
                    extra_data = dict()
                    if 'var' in data:
                        extra_data['var'] = data['var']

                    result.add_edge((v, i + 1), (w, j + 1),
                                    production=psum,
                                    consumption=csum,
                                    tokens=tokens + prates.sum(stop=i) +
                                    crates.sum(start=j + 1),
                                    **extra_data)

    return result
Esempio n. 12
0
def sample_crates(vector, offset, period):
    """ Returns the consumption rates of actor[offset]
    if the consuming actor is unfolded period times.
    NOTE: offset is 0-based
    """
    vector = cyclic(vector)
    pattern = [None] * (len(vector) // gcd( len(vector), period ))
    for i in range(len(pattern)):
        start = offset + 1 + (i - 1) * period
        end = start + period
        pattern[i] = vector.sum(start, end)

    token_delta = pattern[0] - vector.sum(stop = offset + 1)
    return token_delta, cyclic(pattern)
Esempio n. 13
0
def sample_prates(vector, offset, period):
    """ Returns the production rates of unfolded actor [offset]
    if the producing actor is unfolded period times.
    NOTE: offset is 0-based
    """
    vector = cyclic(vector)
    pattern = [None] * (len(vector) // gcd( len(vector), period ))
    for i in range(len(pattern)):
        start = offset + i * period
        end = start + period
        pattern[i] = vector.sum(start, end)

    token_delta = vector.sum(stop = offset)
    return token_delta, cyclic(pattern)
Esempio n. 14
0
def sample_prates(vector, offset, period):
    """ Returns the production rates of unfolded actor [offset]
    if the producing actor is unfolded period times.
    NOTE: offset is 0-based
    """
    vector = cyclic(vector)
    pattern = [None] * (len(vector) // gcd(len(vector), period))
    for i in range(len(pattern)):
        start = offset + i * period
        end = start + period
        pattern[i] = vector.sum(start, end)

    token_delta = vector.sum(stop=offset)
    return token_delta, cyclic(pattern)
Esempio n. 15
0
def sample_crates(vector, offset, period):
    """ Returns the consumption rates of actor[offset]
    if the consuming actor is unfolded period times.
    NOTE: offset is 0-based
    """
    vector = cyclic(vector)
    pattern = [None] * (len(vector) // gcd(len(vector), period))
    for i in range(len(pattern)):
        start = offset + 1 + (i - 1) * period
        end = start + period
        pattern[i] = vector.sum(start, end)

    token_delta = pattern[0] - vector.sum(stop=offset + 1)
    return token_delta, cyclic(pattern)
Esempio n. 16
0
def multi_rate_equivalent( sdfg ):
    result = core.SDFGraph()
    for v, data in sdfg.nodes_iter( data = True ):
        phi = data.get('phases', 1)
        for i in range(phi):
            result.add_node((v, i + 1), wcet = data.get('wcet', core.cyclic(0))[i])

    for v, w, data in sdfg.edges_iter( data = True ):
        Tv = sdfg.node[v].get('phases', 1)
        Tw = sdfg.node[w].get('phases', 1)
        prates = data.get('production', core.cyclic(1))
        crates = data.get('consumption', core.cyclic(1))
        tokens = data.get('tokens', 0)
        plen = len(prates)
        assert Tv == len(prates)
        assert Tw == len(crates)

        # consuming actor in unfolded graph has a single phase
        # and consumption rate equal to crates.sum()
        csum = crates.sum()

        # sum of production rates in multi-rate equivalent of unfolded graph
        psum = prates.sum()

        gvw = gcd( csum, psum )

        # iterate consuming actors
        for j in range(Tw):
            # determine which incoming channels must be added
            # see Algorithm ? in PhD thesis
            for i in range( plen ):
                delta_i_n = tokens + prates.sum(stop = i + 1) - crates.sum(stop = j + 1 )
                delta_i1_n = tokens + prates.sum(stop = i) - crates.sum(stop = j + 1 )
                sol_min = (gvw - delta_i_n - 1) // gvw
                sol_max = (gvw - delta_i1_n - 1) // gvw

                if sol_min < sol_max:
                    # add channel
                    extra_data = dict()
                    if 'var' in data:
                        extra_data['var'] = data['var']

                    result.add_edge( (v, i + 1), (w, j + 1),
                        production = psum,
                        consumption = csum,
                        tokens = tokens + prates.sum(stop = i) + crates.sum(start = j + 1),
                        **extra_data)

    return result
Esempio n. 17
0
def lin_bounds_special(pvec, cvec, tokens):
    prates = core.cyclic(pvec)
    crates = core.cyclic(cvec)

    hi, lo = predecessor_lin_bounds(production=prates,
                                    consumption=crates,
                                    tokens=tokens)

    avgprate = Fraction(prates.sum(), len(prates))
    avgcrate = Fraction(crates.sum(), len(crates))
    g = gcd(prates.sum(), crates.sum())

    gstep = g * len(prates) * len(crates)
    istep = prates.sum() * len(crates)
    jstep = crates.sum() * len(prates)

    i = 0
    base = gstep * (tokens // g)
    argmin = (0, 0)
    argmax = (0, 0)
    minval = maxval = 0
    import pdb
    pdb.set_trace()
    for j in range(0, len(crates)):
        while tokens % g + prates.sum(0, i) - crates.sum(0, j) < g:
            val = ((tokens + prates.sum(0, i) - crates.sum(0, j)) //
                   g) * gstep - i * istep + j * jstep
            if val < minval:
                minval = val
                argmin = (i, j)
            i = i + 1

    j = 0
    for i in range(0, len(prates)):
        while tokens % g + prates.sum(0, i) - crates.sum(0, j) >= 0:
            val = ((tokens + prates.sum(0, i) - crates.sum(0, j)) //
                   g) * gstep - i * istep + j * jstep
            if val > maxval:
                maxval = val
                argmax = (i, j)
            j = j + 1

    minval = Fraction(base + minval, len(prates) * len(crates))
    maxval = Fraction(base + maxval, len(prates) * len(crates))
    assert minval + g - avgprate == lo, "{} != {}".format(
        minval + g - avgprate, lo)
    assert maxval == hi, "{} != {}".format(maxval, hi)

    return argmin, argmax, lo, hi
Esempio n. 18
0
def analyse_cycles(sdfg):
    vectors = core.check_consistency( sdfg )
    s = vectors['s']
    q = vectors['q']
    print("HSDF graph size: {}".format( sum(q.values()) ))
    par = {}
    for cycle in nx.simple_cycles( sdfg ):
        edges = [ (cycle[i - 1], cycle[i]) for i in range(len(cycle)) ]
        wtsum = 0
        multiple = 1
        z = {}
        for v, w in edges:
            data = sdfg.get_edge_data( v, w )
            tokens = data.get('tokens', 0)
            prates = data.get('production', core.cyclic(1))

            wtsum += s[ (v, w) ] * tokens
            z[v] = prates.sum() * s[ (v, w) ]
            multiple = core.lcm( multiple, z[v] )

        if wtsum % multiple == 0:
            for v in cycle:
                parv = wtsum // z[ v ]
                par[v] = parv if v not in par else min(par[v], parv)

        print("Cycle {}: tokens = {:.3f}, integral: {}".format( cycle, wtsum / multiple, wtsum % multiple == 0 ))

    for v in par:
        if q[v] % par[v] == 0:
            q[v] = q[v] // par[v]
        elif par[v] % q[v] == 0:
            q[v] = 1
    
    print("New HSDF graph size: {}".format( sum(q.values()) ))
Esempio n. 19
0
def analyse_cycles(sdfg):
    vectors = core.check_consistency(sdfg)
    s = vectors['s']
    q = vectors['q']
    print("HSDF graph size: {}".format(sum(q.values())))
    par = {}
    for cycle in nx.simple_cycles(sdfg):
        edges = [(cycle[i - 1], cycle[i]) for i in range(len(cycle))]
        wtsum = 0
        multiple = 1
        z = {}
        for v, w in edges:
            data = sdfg.get_edge_data(v, w)
            tokens = data.get('tokens', 0)
            prates = data.get('production', core.cyclic(1))

            wtsum += s[(v, w)] * tokens
            z[v] = prates.sum() * s[(v, w)]
            multiple = core.lcm(multiple, z[v])

        if wtsum % multiple == 0:
            for v in cycle:
                parv = wtsum // z[v]
                par[v] = parv if v not in par else min(par[v], parv)

        print("Cycle {}: tokens = {:.3f}, integral: {}".format(
            cycle, wtsum / multiple, wtsum % multiple == 0))

    for v in par:
        if q[v] % par[v] == 0:
            q[v] = q[v] // par[v]
        elif par[v] % q[v] == 0:
            q[v] = 1

    print("New HSDF graph size: {}".format(sum(q.values())))
Esempio n. 20
0
def incremental_cycle_analysis(g, cycle):
    if not g.is_consistent():
        raise ValueError("Graph is not consistent")

    # while there are channels with non-linear predecessor functions, retime & vectorise
    while True:
        updated = False
        apx_opt = single_rate_apx(g, False)
        apx_pess = single_rate_apx(g, True)

        for a, b in cycle:
            max_tokens = apx_opt.get_edge_data(a, b).get('tokens', 0)
            min_tokens = apx_pess.get_edge_data(a, b).get('tokens', 0)
            data = g.get_edge_data(a, b)
            prates = data.get('production', cyclic(1))
            crates = data.get('consumption', cyclic(1))
            tokens = data.get('tokens', 0)
            if prates.sum() * len(crates) >= crates.sum() * len(
                    prates) and min_tokens < max_tokens:

                r, va, vb = retime_vectorise(g, a, b)
                print("Vectorised {} -> vector = {}, {}, retiming = {}".format(
                    (a, b), va, vb, r))
                fire(g, {b: r})
                g = parallelise(g, {a: vb, b: vb})

                apx_opt = single_rate_apx(g, False)
                apx_pess = single_rate_apx(g, True)

                updated = True
                break

        tokens_lo, tokens_hi, weight = 0, 0, 0
        for a, b in cycle:
            max_tokens = apx_opt.get_edge_data(a, b).get('tokens', 0)
            min_tokens = apx_pess.get_edge_data(a, b).get('tokens', 0)
            tokens_lo = tokens_lo + min_tokens
            tokens_hi = tokens_hi + max_tokens
            weight = weight + apx_pess.node[b].get('wcet', 0)

        assert g.is_consistent()
        print("\nMCR Bounds: [ {}, {} ]".format(g.tpi * weight / tokens_lo,
                                                g.tpi * weight / tokens_hi))

        if not updated:
            break
Esempio n. 21
0
def normalise_channels( g ):
    result = core.SDFGraph()
    for v, data in g.nodes_iter( data = True ):
        result.add_node( v, **data )

    for u, v, data in g.edges_iter( data = True ):
        prates = data.get('production', cyclic(1))
        crates = data.get('consumption', cyclic(1))
        tokens = data.get('tokens', 0)

        if len(prates) == len(crates) == 1:
            g = gcd( prates[0], crates[0] )
            norm_data = dict( production = prates[0] // g, consumption = crates[0] // g, tokens = tokens // g )
            result.add_edge( u, v, **norm_data )
        else:
            result.add_edge( u, v, **data )

    return result
Esempio n. 22
0
def lin_bounds_special( pvec, cvec, tokens ):
    prates = core.cyclic( pvec )
    crates = core.cyclic( cvec )

    hi, lo = predecessor_lin_bounds( production = prates, consumption = crates, tokens = tokens )

    avgprate = Fraction( prates.sum(), len(prates))
    avgcrate = Fraction( crates.sum(), len(crates))
    g = gcd( prates.sum(), crates.sum() )

    gstep = g * len(prates) * len(crates)
    istep = prates.sum() * len(crates)
    jstep = crates.sum() * len(prates)

    i = 0
    base = gstep * (tokens // g)
    argmin = (0, 0)
    argmax = (0, 0)
    minval = maxval = 0
    import pdb; pdb.set_trace()
    for j in range(0, len(crates)):
        while tokens % g + prates.sum(0, i) - crates.sum(0, j) < g:
            val = ((tokens + prates.sum(0, i) - crates.sum(0, j)) // g) * gstep - i * istep + j * jstep
            if val < minval:
                minval = val
                argmin = (i, j)
            i = i + 1

    j = 0
    for i in range(0, len(prates)):
        while tokens % g + prates.sum(0, i) - crates.sum(0, j) >= 0:
            val = ((tokens + prates.sum(0, i) - crates.sum(0, j)) // g) * gstep - i * istep + j * jstep
            if val > maxval:
                maxval = val
                argmax = (i, j)
            j = j + 1

    minval = Fraction(base + minval, len(prates) * len(crates))
    maxval = Fraction(base + maxval, len(prates) * len(crates))
    assert minval + g - avgprate == lo, "{} != {}".format(minval + g - avgprate, lo)
    assert maxval == hi, "{} != {}".format(maxval, hi)

    return argmin, argmax, lo, hi
Esempio n. 23
0
def apply_vars(g, bindings):
    result = core.SDFGraph()
    for v, data in g.nodes_iter( data = True ):
        result.add_node( v, wcet = data['wcet'] )

    for v, w, data in g.edges_iter( data = True ):
        prates = data.get('production', cyclic(1))
        crates = data.get('consumption', cyclic(1))
        tokens = data.get('tokens', 0)
        if 'var' in data:
            var = data.get('var')
            tokens = tokens + bindings.get(var, 0)

        result.add_edge( v, w,
            production = prates,
            consumption = crates,
            tokens = tokens)

    return result        
Esempio n. 24
0
def incremental_cycle_analysis(g, cycle):
    if not g.is_consistent():
        raise ValueError("Graph is not consistent")

    # while there are channels with non-linear predecessor functions, retime & vectorise
    while True:
        updated = False
        apx_opt = single_rate_apx( g, False )
        apx_pess = single_rate_apx( g, True )

        for a, b in cycle:
            max_tokens = apx_opt.get_edge_data( a, b ).get('tokens', 0)
            min_tokens = apx_pess.get_edge_data( a, b ).get('tokens', 0)
            data = g.get_edge_data( a, b )
            prates = data.get('production', cyclic(1))
            crates = data.get('consumption', cyclic(1))
            tokens = data.get('tokens', 0)
            if prates.sum() * len(crates) >= crates.sum() * len(prates) and min_tokens < max_tokens:

                r, va, vb = retime_vectorise( g, a, b )
                print("Vectorised {} -> vector = {}, {}, retiming = {}".format( (a, b), va, vb, r))
                fire(g, {b: r})
                g = parallelise( g, {a: vb, b: vb} )

                apx_opt = single_rate_apx( g, False )
                apx_pess = single_rate_apx( g, True )

                updated = True
                break

        tokens_lo, tokens_hi, weight = 0, 0, 0
        for a, b in cycle:
            max_tokens = apx_opt.get_edge_data( a, b ).get('tokens', 0)
            min_tokens = apx_pess.get_edge_data( a, b ).get('tokens', 0)
            tokens_lo = tokens_lo + min_tokens
            tokens_hi = tokens_hi + max_tokens
            weight = weight + apx_pess.node[ b ].get('wcet', 0)

        assert g.is_consistent()
        print("\nMCR Bounds: [ {}, {} ]".format( g.tpi * weight / tokens_lo, g.tpi * weight / tokens_hi ))

        if not updated:
            break
Esempio n. 25
0
def tt_bounds( g, u, v, w ):
    assert g.is_consistent()
    suv = g.s[(u, v)]
    svw = g.s[(v, w)]
    tokens = g.get_edge_data(u, v).get('tokens', 0)
    prates = g.get_edge_data(v, w).get('production', cyclic(1))
    crates = g.get_edge_data(u, v).get('consumption', cyclic(1))
    phi = g.node[v].get('phases')
    slope = Fraction( crates.sum(), prates.sum() )
    
    minval = None
    maxval = None
    for i in range( phi ):
        value = svw * sum(prates[:i]) + suv * (tokens - sum(crates[:i + 1]))
        minval = value if minval is None else min(value, minval)

        value = svw * sum(prates[:i]) + suv * (tokens - sum(crates[:i]))
        maxval = value if maxval is None else max(value, maxval)

    return minval + svw, maxval
Esempio n. 26
0
def apply_vars(g, bindings):
    result = core.SDFGraph()
    for v, data in g.nodes_iter(data=True):
        result.add_node(v, wcet=data['wcet'])

    for v, w, data in g.edges_iter(data=True):
        prates = data.get('production', cyclic(1))
        crates = data.get('consumption', cyclic(1))
        tokens = data.get('tokens', 0)
        if 'var' in data:
            var = data.get('var')
            tokens = tokens + bindings.get(var, 0)

        result.add_edge(v,
                        w,
                        production=prates,
                        consumption=crates,
                        tokens=tokens)

    return result
Esempio n. 27
0
def normalise_channels(g):
    result = core.SDFGraph()
    for v, data in g.nodes_iter(data=True):
        result.add_node(v, **data)

    for u, v, data in g.edges_iter(data=True):
        prates = data.get('production', cyclic(1))
        crates = data.get('consumption', cyclic(1))
        tokens = data.get('tokens', 0)

        if len(prates) == len(crates) == 1:
            g = gcd(prates[0], crates[0])
            norm_data = dict(production=prates[0] // g,
                             consumption=crates[0] // g,
                             tokens=tokens // g)
            result.add_edge(u, v, **norm_data)
        else:
            result.add_edge(u, v, **data)

    return result
Esempio n. 28
0
def token_predecessor_bounds( g, uv, vw ):
    assert uv[1] == vw[0]
    assert g.is_consistent()
    u, v = uv
    _, w = vw
    suv = g.s[ uv ]
    svw = g.s[ vw ]

    arg = suv * g.get_edge_data(u, v).get('tokens', 0)
    prates = g.get_edge_data(v, w).get('production', cyclic(1))
    crates = g.get_edge_data(u, v).get('consumption', cyclic(1))

    minarg, maxarg = None, None
    for i in range( g.node[v].get('phases')):
        maxarg = max( maxarg, arg ) if maxarg is not None else arg
        arg -= suv * crates[i]
        minarg = min( minarg, arg + svw * prates.sum()) if minarg is not None else arg + svw * prates.sum()
        arg += svw * prates[i]

    return minarg, maxarg
Esempio n. 29
0
def lin_bounds(g, v, w):
    tokens = g.get_edge_data(v, w).get('tokens', 0)
    prates = g.get_edge_data(v, w).get('production', cyclic(1))
    crates = g.get_edge_data(v, w).get('consumption', cyclic(1))
    phi_v = g.node[v].get('phases')
    phi_w = g.node[w].get('phases')
    g_vw = gcd( prates.sum(), crates.sum() )
    qv = g.q[v]
    qw = g.q[w]
    minval = None
    maxval = None

    for i in range( phi_v ):
        for j in range( phi_w ):
            value = Fraction( g_vw * phi_v * (tokens + sum(prates[:i]) - sum(crates[:j])) // g_vw, prates.sum()) - i + j * Fraction(qv, qw)
            maxval = value if maxval is None else max(value, maxval)
            value = Fraction( g_vw * phi_v * (tokens + g_vw + sum(prates[:i]) - sum(crates[:j])) // g_vw, prates.sum()) - i + j * Fraction(qv, qw)
            minval = value if minval is None else min(value, minval)

    return minval - 1, maxval
Esempio n. 30
0
def tt_bounds(g, u, v, w):
    assert g.is_consistent()
    suv = g.s[(u, v)]
    svw = g.s[(v, w)]
    tokens = g.get_edge_data(u, v).get('tokens', 0)
    prates = g.get_edge_data(v, w).get('production', cyclic(1))
    crates = g.get_edge_data(u, v).get('consumption', cyclic(1))
    phi = g.node[v].get('phases')
    slope = Fraction(crates.sum(), prates.sum())

    minval = None
    maxval = None
    for i in range(phi):
        value = svw * sum(prates[:i]) + suv * (tokens - sum(crates[:i + 1]))
        minval = value if minval is None else min(value, minval)

        value = svw * sum(prates[:i]) + suv * (tokens - sum(crates[:i]))
        maxval = value if maxval is None else max(value, maxval)

    return minval + svw, maxval
Esempio n. 31
0
def test_lin_bounds_special(s=200, a=2, b=40, dbg=None):
    from syncdataflow.randomsdf import random_vector
    from random import randint, seed
    import pdb
    for k in [dbg] if dbg else range(1, 10000):
        seed(k)
        prates = core.cyclic(random_vector(s, randint(a, b)))
        crates = core.cyclic(random_vector(s, randint(a, b)))
        tokens = randint(0, 100)

        hi, lo = predecessor_lin_bounds(production=prates,
                                        consumption=crates,
                                        tokens=tokens)

        avgprate = Fraction(prates.sum(), len(prates))
        avgcrate = Fraction(crates.sum(), len(crates))
        g = gcd(prates.sum(), crates.sum())
        if dbg: pdb.set_trace()

        i = 0
        minval = maxval = g * (tokens // g)
        for j in range(0, len(crates)):
            while tokens % g + prates.sum(0, i) - crates.sum(0, j) < g:
                val = ((tokens + prates.sum(0, i) - crates.sum(0, j)) //
                       g) * g - i * avgprate + j * avgcrate
                minval = min(minval, val)
                i = i + 1

        j = 0
        for i in range(0, len(prates)):
            while tokens % g + prates.sum(0, i) - crates.sum(0, j) >= 0:
                val = ((tokens + prates.sum(0, i) - crates.sum(0, j)) //
                       g) * g - i * avgprate + j * avgcrate
                maxval = max(maxval, val)
                j = j + 1

        assert minval + g - avgprate == lo, "{} != {}. seed = {}".format(
            minval + g - avgprate, lo, k)
        assert maxval == hi, "{} != {}. seed = {}".format(maxval, hi, k)

    print("PASS")
Esempio n. 32
0
def parallelise(g, vectors=None, **kwargs):
    vectors = vectors or kwargs
    result = core.SDFGraph()
    for v, data in g.nodes_iter(data=True):
        parallelism = core.cyclic(vectors.get(v, [1]))
        wcet = data.get('wcet')
        assert len(
            parallelism
        ) > 0, "Invalid parallelism for {}: {} (vectors: {})".format(
            v, parallelism, vectors)
        result.add_node(v, wcet=wcet * len(parallelism))

    for v, w, data in g.edges_iter(data=True):
        prates = data.get('production', core.cyclic(1))
        crates = data.get('consumption', core.cyclic(1))
        tokens = data.get('tokens', 0)

        parallelism_v = core.cyclic(vectors.get(v, 1))
        parallelism_w = core.cyclic(vectors.get(w, 1))

        s = 0
        new_prates = list()
        for i in range((len(prates) * len(parallelism_v)) //
                       gcd(len(prates), sum(parallelism_v))):
            new_prates.append(prates.sum(s, s + parallelism_v[i]))
            s = s + parallelism_v[i]

        s = 0
        new_crates = list()
        for i in range((len(crates) * len(parallelism_w)) //
                       gcd(len(crates), sum(parallelism_w))):
            new_crates.append(crates.sum(s, s + parallelism_w[i]))
            s = s + parallelism_w[i]

        result.add_edge(v,
                        w,
                        production=new_prates,
                        consumption=new_crates,
                        tokens=tokens)

    return result
Esempio n. 33
0
def retime_vectorise(g, a, b):
    """ Reimes and vectorises actor b, such that its throughput-critical parallelism is enforced.
    NOTE: Assumes that the execution time of actor b is constant.

    Parameters:
    g       core.SDFGraph
    a       source actor
    b       target actor

    Returns a tuple (r, v), where r is the number of firings of b that were simulated, and
    v is the blocking vector used in the vectorisation.
    """

    data = g.get_edge_data(a, b)
    prates = data.get('production', cyclic(1))
    crates = data.get('consumption', cyclic(1))
    tokens = data.get('tokens', 0)

    periods = (crates.sum() // gcd(prates.sum(), crates.sum())) * len(prates)
    cons_q = (prates.sum() // gcd(prates.sum(), crates.sum())) * len(crates)
    firings = 0
    while (tokens % crates.sum(0, cons_q)) >= crates.sum(0, firings + 1):
        firings = firings + 1

    j = firings + (tokens // crates.sum(0, cons_q)) * cons_q
    blocking_vector = list()
    blocking_a = list()
    i0 = 0
    for i in range(periods):
        k = j
        while sum(crates[:k + 1]) - sum(prates[:i + 1]) <= tokens:
            k = k + 1
        if k > j:
            blocking_vector.append(k - j)
            j = k
            blocking_a.append(i + 1 - i0)
            i0 = i + 1

    return firings, blocking_a, blocking_vector
Esempio n. 34
0
def retime_vectorise(g, a, b):
    """ Reimes and vectorises actor b, such that its throughput-critical parallelism is enforced.
    NOTE: Assumes that the execution time of actor b is constant.

    Parameters:
    g       core.SDFGraph
    a       source actor
    b       target actor

    Returns a tuple (r, v), where r is the number of firings of b that were simulated, and
    v is the blocking vector used in the vectorisation.
    """

    data = g.get_edge_data(a, b)
    prates = data.get('production', cyclic(1))
    crates = data.get('consumption', cyclic(1))
    tokens = data.get('tokens', 0)

    periods = (crates.sum() // gcd( prates.sum(), crates.sum())) * len(prates)
    cons_q = (prates.sum() // gcd( prates.sum(), crates.sum())) * len(crates)
    firings = 0
    while (tokens % crates.sum(0, cons_q)) >= crates.sum(0, firings + 1):
        firings = firings + 1

    j = firings + ( tokens // crates.sum(0, cons_q )) * cons_q
    blocking_vector = list()
    blocking_a = list()
    i0 = 0
    for i in range(periods):
        k = j
        while sum(crates[:k + 1]) - sum(prates[:i + 1]) <= tokens:
            k = k + 1
        if k > j:
            blocking_vector.append( k - j )
            j = k
            blocking_a.append( i + 1 - i0 )
            i0 = i + 1

    return firings, blocking_a, blocking_vector
Esempio n. 35
0
def token_predecessor_bounds(g, uv, vw):
    assert uv[1] == vw[0]
    assert g.is_consistent()
    u, v = uv
    _, w = vw
    suv = g.s[uv]
    svw = g.s[vw]

    arg = suv * g.get_edge_data(u, v).get('tokens', 0)
    prates = g.get_edge_data(v, w).get('production', cyclic(1))
    crates = g.get_edge_data(u, v).get('consumption', cyclic(1))

    minarg, maxarg = None, None
    for i in range(g.node[v].get('phases')):
        maxarg = max(maxarg, arg) if maxarg is not None else arg
        arg -= suv * crates[i]
        minarg = min(
            minarg, arg + svw *
            prates.sum()) if minarg is not None else arg + svw * prates.sum()
        arg += svw * prates[i]

    return minarg, maxarg
Esempio n. 36
0
def fire(sdfg, firings_dict=None, **attr):
    """ Fires actors in the SDF graph sdfg.
    Which actors to fire how many times is specified either as keyword arguments, or through
    the dictionary firings_dict.
    Keys specify which actors to fire, values specify how many times.
    Negative values "rewind" firings.

    NOTE: rate vectors are updated accordingly: if an actor with production rate vector [a,b,c]
    fires once, then the production rate vector will have changed into [b,c,a]
    """
    if firings_dict is not None:
        attr.update(firings_dict)

    for v in attr:
        firings = attr[v]
        if firings > 0:
            for _, w, data in sdfg.out_edges_iter(v, True):
                prates = data.get('production', cyclic(1))
                data['tokens'] = data.get('tokens', 0) + prates.sum(0, firings)
                data['production'] = prates[firings:]

            for v, _, data in sdfg.in_edges_iter(v, True):
                crates = data.get('consumption', cyclic(1))
                data['tokens'] = data.get('tokens', 0) - crates.sum(0, firings)
                data['consumption'] = crates[firings:]

        elif firings < 0:
            # negative indices run from -1 to and including firings
            # subtract one to have correct bounds for the slices used below
            for _, w, data in sdfg.out_edges_iter(v, True):
                prates = data.get('production', cyclic(1))
                data['tokens'] = data.get('tokens', 0) - prates.sum(firings, 0)
                data['production'] = prates[firings:]

            for v, _, data in sdfg.in_edges_iter(v, True):
                crates = data.get('consumption', cyclic(1))
                data['tokens'] = data.get('tokens', 0) + crates.sum(firings, 0)
                data['consumption'] = crates[firings:]
Esempio n. 37
0
def fire( sdfg, firings_dict = None, **attr ):
    """ Fires actors in the SDF graph sdfg.
    Which actors to fire how many times is specified either as keyword arguments, or through
    the dictionary firings_dict.
    Keys specify which actors to fire, values specify how many times.
    Negative values "rewind" firings.

    NOTE: rate vectors are updated accordingly: if an actor with production rate vector [a,b,c]
    fires once, then the production rate vector will have changed into [b,c,a]
    """
    if firings_dict is not None:
        attr.update( firings_dict )

    for v in attr:
        firings = attr[ v ]
        if firings > 0:
            for _, w, data in sdfg.out_edges_iter( v, True ):
                prates = data.get('production', cyclic(1))
                data['tokens'] = data.get('tokens', 0) + prates.sum(0,firings)
                data['production'] = prates[firings:]

            for v, _, data in sdfg.in_edges_iter( v, True ):
                crates = data.get('consumption', cyclic(1))
                data['tokens'] = data.get('tokens', 0) - crates.sum(0, firings)
                data['consumption'] = crates[firings:]

        elif firings < 0:
            # negative indices run from -1 to and including firings
            # subtract one to have correct bounds for the slices used below
            for _, w, data in sdfg.out_edges_iter( v, True ):
                prates = data.get('production', cyclic(1))
                data['tokens'] = data.get('tokens', 0) - prates.sum(firings, 0)
                data['production'] = prates[firings:]

            for v, _, data in sdfg.in_edges_iter( v, True ):
                crates = data.get('consumption', cyclic(1))
                data['tokens'] = data.get('tokens', 0) + crates.sum(firings, 0)
                data['consumption'] = crates[firings:]
Esempio n. 38
0
def undo_vectorisation(g, actor, vec):
    result = core.SDFGraph()
    for v, data in g.nodes_iter(data=True):
        if v == actor:
            wcet = data.get('wcet')[0]
        else:
            wcet = data.get('wcet')

        result.add_node(v, wcet=wcet)

    for v, w, data in g.edges_iter(data=True):
        prates = data.get('production', core.cyclic(1))
        crates = data.get('consumption', core.cyclic(1))
        tokens = data.get('tokens', 0)

        if v == actor:
            assert len(vec) == len(prates)
            assert prates[0] % vec[0] == 0
            rate = prates[0] // vec[0]
            for a, b in zip(vec, prates):
                assert b % a == 0 and b // a == rate
            prates = core.cyclic(rate)

        elif w == actor:
            assert len(vec) == len(crates)
            assert crates[0] % vec[0] == 0
            rate = crates[0] // vec[0]
            for a, b in zip(vec, crates):
                assert b % a == 0 and b // a == rate
            crates = core.cyclic(rate)

        result.add_edge(v,
                        w,
                        production=prates,
                        consumption=crates,
                        tokens=tokens)

    return result
Esempio n. 39
0
def gather_variables(csdfg):
    variables = dict()
    for u, v, data in csdfg.edges_iter(data=True):
        if __PARAM_VAR__ in data:
            var = data[__PARAM_VAR__]
            assert var not in variables, "Each variable can only occur once"

            # gather moduli
            prates = data.get(__PARAM_PRATES__, core.cyclic(1))
            crates = data.get(__PARAM_CRATES__, core.cyclic(1))
            tokens = data.get(__PARAM_TOKENS__, 0)

            moduli = set()
            guv = gcd(prates.sum(), crates.sum())
            for prate in prates:
                for crate in crates:
                    moduli.add(guv - (tokens % guv))
                    tokens = tokens - crate
                tokens = tokens + prate + crates.sum()

            variables[var] = (guv, sorted(moduli))

    return variables
Esempio n. 40
0
def gather_variables( csdfg ):
    variables = dict()
    for u, v, data in csdfg.edges_iter( data = True ):
        if __PARAM_VAR__ in data:
            var = data[ __PARAM_VAR__ ]
            assert var not in variables, "Each variable can only occur once"

            # gather moduli
            prates = data.get( __PARAM_PRATES__, core.cyclic(1))
            crates = data.get( __PARAM_CRATES__, core.cyclic(1))
            tokens = data.get( __PARAM_TOKENS__, 0 )

            moduli = set()
            guv = gcd( prates.sum(), crates.sum())
            for prate in prates:
                for crate in crates:
                    moduli.add( guv - (tokens % guv))
                    tokens = tokens - crate
                tokens = tokens + prate + crates.sum()

            variables[ var ] = ( guv, sorted(moduli) )

    return variables
Esempio n. 41
0
def single_rate_equivalent( sdfg ):
    if not sdfg.is_consistent():
        raise ValueError('Inconsistent SDF graph')

    hsdfg = nx.DiGraph()
    for v in sdfg.q:
        assert 'wcet' in sdfg.node[v], "Missing 'wcet' attribute for node {}".format(v)
        wcets = cyclic(sdfg.node[v]['wcet'])

        for i in range(sdfg.q[v]):
            hsdfg.add_node( (v, i + 1), wcet = wcets[i % len(wcets)] )

        for u, _, data in sdfg.in_edges_iter( v, True ):
            prates = data.get('production', cyclic(1))
            crates = data.get('consumption', cyclic(1))
            tokens = data.get('tokens', 0)
            for j in range(sdfg.q[v]):
                extra_data = dict()
                i = predecessor(j + 1, **data)
                hsdfg.add_edge( (u, (i - 1) % sdfg.q[u] + 1), (v, j + 1), tokens = (sdfg.q[u] - i) // sdfg.q[u], **extra_data )

    assert hsdfg.number_of_nodes() == sum(sdfg.q.values()), "Size of HSDF graph is equal to sum of repetition vector"
    return hsdfg
Esempio n. 42
0
def undo_vectorisation( g, actor, vec ):
    result = core.SDFGraph()
    for v, data in g.nodes_iter( data = True ):
        if v == actor:
            wcet = data.get('wcet')[0]
        else:
            wcet = data.get('wcet')

        result.add_node( v, wcet = wcet )

    for v, w, data in g.edges_iter( data = True ):
        prates = data.get('production', core.cyclic(1))
        crates = data.get('consumption', core.cyclic(1))
        tokens = data.get('tokens', 0)

        if v == actor:
            assert len( vec ) == len( prates )
            assert prates[0] % vec[0] == 0
            rate = prates[ 0 ] // vec[ 0 ]
            for a, b in zip( vec, prates ):
                assert b % a == 0 and b // a == rate
            prates = core.cyclic( rate )

        elif w == actor:
            assert len( vec ) == len( crates )
            assert crates[0] % vec[0] == 0
            rate = crates[ 0 ] // vec[ 0 ]
            for a, b in zip( vec, crates ):
                assert b % a == 0 and b // a == rate
            crates = core.cyclic( rate )

        result.add_edge( v, w,
            production = prates,
            consumption = crates,
            tokens = tokens )

    return result
Esempio n. 43
0
def test_lin_bounds_special( s = 200, a = 2, b = 40, dbg = None ):
    from syncdataflow.randomsdf import random_vector
    from random import randint, seed
    import pdb
    for k in [dbg] if dbg else range(1, 10000):
        seed(k)
        prates = core.cyclic( random_vector( s, randint(a, b)))
        crates = core.cyclic( random_vector( s, randint(a, b)))
        tokens = randint( 0, 100 )

        hi, lo = predecessor_lin_bounds( production = prates, consumption = crates, tokens = tokens )

        avgprate = Fraction( prates.sum(), len(prates))
        avgcrate = Fraction( crates.sum(), len(crates))
        g = gcd( prates.sum(), crates.sum() )
        if dbg: pdb.set_trace()

        i = 0
        minval = maxval = g * (tokens // g)
        for j in range(0, len(crates)):
            while tokens % g + prates.sum(0, i) - crates.sum(0, j) < g:
                val = ((tokens + prates.sum(0, i) - crates.sum(0, j)) // g) * g - i * avgprate + j * avgcrate
                minval = min( minval, val )
                i = i + 1

        j = 0
        for i in range(0, len(prates)):
            while tokens % g + prates.sum(0, i) - crates.sum(0, j) >= 0:
                val = ((tokens + prates.sum(0, i) - crates.sum(0, j)) // g) * g - i * avgprate + j * avgcrate
                maxval = max( maxval, val )
                j = j + 1

        assert minval + g - avgprate == lo, "{} != {}. seed = {}".format(minval + g - avgprate, lo, k)
        assert maxval == hi, "{} != {}. seed = {}".format(maxval, hi, k)

    print("PASS")
Esempio n. 44
0
def pessimistic_bottlenecks(**kwargs):
    prates = kwargs.get('production', cyclic(1))
    crates = kwargs.get('consumption', cyclic(1))
    tokens = kwargs.get('tokens', 0)
    g = gcd(prates.sum(), crates.sum())
    avg_prate = Fraction(prates.sum(), len(prates))
    avg_crate = Fraction(crates.sum(), len(crates))
    minval = None
    maxval = None
    arg_i = None
    arg_j = None
    delta = tokens
    for i in range(len(prates)):
        for j in range(len(crates)):
            val_ij = g * (delta // g) - i * avg_prate + j * avg_crate

            if minval is None or val_ij < minval:
                minval = val_ij
                arg_i = i + 1
                arg_j = j + 1
            delta -= crates[j]
        delta = delta + prates[i] + crates.sum()

    return minval + g - avg_prate, arg_i, arg_j
Esempio n. 45
0
def pessimistic_bottlenecks(**kwargs):
    prates = kwargs.get('production', cyclic(1))
    crates = kwargs.get('consumption', cyclic(1))
    tokens = kwargs.get('tokens', 0)
    g = gcd(prates.sum(), crates.sum())
    avg_prate = Fraction( prates.sum(), len(prates) )
    avg_crate = Fraction( crates.sum(), len(crates) )
    minval = None
    maxval = None
    arg_i = None
    arg_j = None
    delta = tokens
    for i in range(len(prates)):
        for j in range(len(crates)):
            val_ij = g * (delta // g) - i * avg_prate + j * avg_crate

            if minval is None or val_ij < minval:
                minval = val_ij
                arg_i = i + 1
                arg_j = j + 1
            delta -= crates[j]
        delta = delta + prates[i] + crates.sum()

    return minval + g - avg_prate, arg_i, arg_j
Esempio n. 46
0
def incremental_graph_analysis( g ):
    if not g.is_consistent():
        raise ValueError("Graph is not consistent")

    blocking_vectors = dict()
    while True:
        assert g.is_consistent()
        # compute single-rate approximations
        print("Constructing single-rate approximations...", end = "")
        apx_opt = single_rate_apx( g, False )
        apx_pess = single_rate_apx( g, True )
        print("done")

        # find critical cycle in pessimistic approximation
        print("Analysing pessimistic approximation...", end = "")
        mg = as_marked_graph( apx_pess )
        try:
            ratio, cycle, _ = mcr.compute_mcr( mg )
        except mcr.InfeasibleException as ex:
            ratio, cycle = None, ex.cycle

        # resolve conflicts in parallelism
        print("done. Critical cycle: {}".format( cycle ))
        for a, b in cycle:
            if b in blocking_vectors:
                v, r, bv = blocking_vectors[ b ]
                if v != a:
                    # undo vectorisation
                    print("Undoing vectorisation of {} by vector {}".format(b, bv))
                    g = undo_vectorisation( g, b, bv )

                    # undo retiming
                    print("Retiming {} by simulating {} firings".format(b, -r))
                    fire( g, {b: -r})

                    # unfold a
                    print("Unfolding {} into {} actors".format(b, sum(bv)))
                    g = unfold( g, {b: sum(bv )})

                    del blocking_vectors[ b ]
                    break
        else:
            # perform incremental cycle analysis, stepwise
            for a, b in cycle:
                data = g.get_edge_data( a, b )
                prates = data.get('production', cyclic(1))
                crates = data.get('consumption', cyclic(1))
                if (prates.sum() * len( crates ) < crates.sum() * len( prates )):
                    # gain is smaller than one -> can't vectorise
                    continue

                max_tokens = apx_opt.get_edge_data( a, b ).get('tokens', 0)
                min_tokens = apx_pess.get_edge_data( a, b ).get('tokens', 0)
                if (max_tokens > min_tokens):
                    print("Channel ({}, {}): {} --> {}, norm.token-diff = {}".format(a, b, prates, crates, g.s[(a, b)] * ( max_tokens - min_tokens )))

            for a, b in cycle:
                data = g.get_edge_data( a, b )
                prates = data.get('production', cyclic(1))
                crates = data.get('consumption', cyclic(1))
                if (prates.sum() * len( crates ) < crates.sum() * len( prates )):
                    # gain is smaller than one -> can't vectorise
                    continue

                max_tokens = apx_opt.get_edge_data( a, b ).get('tokens', 0)
                min_tokens = apx_pess.get_edge_data( a, b ).get('tokens', 0)
                if (max_tokens > min_tokens):
                    print("Channel ({}, {}): production = {}, consumption = {}, token-diff = {}, vectorising consumer".format(a, b, prates, crates, max_tokens - min_tokens ))
                    r, v = retime_vectorise( g, a, b )
                    if r > 0:
                        print("Retiming {} by simulating {} firings".format(b, r))

                    print("Vectorising {} by vector {}...".format(b, v), end = "")
                    blocking_vectors[ b ] = a, r, v
                    g = parallelise( g, {b: v} )
                    print("done")
                    break
            else:
                # approximation is exact
                break

    return g
Esempio n. 47
0
def incremental_graph_analysis(g):
    if not g.is_consistent():
        raise ValueError("Graph is not consistent")

    blocking_vectors = dict()
    while True:
        assert g.is_consistent()
        # compute single-rate approximations
        print("Constructing single-rate approximations...", end="")
        apx_opt = single_rate_apx(g, False)
        apx_pess = single_rate_apx(g, True)
        print("done")

        # find critical cycle in pessimistic approximation
        print("Analysing pessimistic approximation...", end="")
        mg = as_marked_graph(apx_pess)
        try:
            ratio, cycle, _ = mcr.compute_mcr(mg)
        except mcr.InfeasibleException as ex:
            ratio, cycle = None, ex.cycle

        # resolve conflicts in parallelism
        print("done. Critical cycle: {}".format(cycle))
        for a, b in cycle:
            if b in blocking_vectors:
                v, r, bv = blocking_vectors[b]
                if v != a:
                    # undo vectorisation
                    print("Undoing vectorisation of {} by vector {}".format(
                        b, bv))
                    g = undo_vectorisation(g, b, bv)

                    # undo retiming
                    print("Retiming {} by simulating {} firings".format(b, -r))
                    fire(g, {b: -r})

                    # unfold a
                    print("Unfolding {} into {} actors".format(b, sum(bv)))
                    g = unfold(g, {b: sum(bv)})

                    del blocking_vectors[b]
                    break
        else:
            # perform incremental cycle analysis, stepwise
            for a, b in cycle:
                data = g.get_edge_data(a, b)
                prates = data.get('production', cyclic(1))
                crates = data.get('consumption', cyclic(1))
                if (prates.sum() * len(crates) < crates.sum() * len(prates)):
                    # gain is smaller than one -> can't vectorise
                    continue

                max_tokens = apx_opt.get_edge_data(a, b).get('tokens', 0)
                min_tokens = apx_pess.get_edge_data(a, b).get('tokens', 0)
                if (max_tokens > min_tokens):
                    print("Channel ({}, {}): {} --> {}, norm.token-diff = {}".
                          format(a, b, prates, crates,
                                 g.s[(a, b)] * (max_tokens - min_tokens)))

            for a, b in cycle:
                data = g.get_edge_data(a, b)
                prates = data.get('production', cyclic(1))
                crates = data.get('consumption', cyclic(1))
                if (prates.sum() * len(crates) < crates.sum() * len(prates)):
                    # gain is smaller than one -> can't vectorise
                    continue

                max_tokens = apx_opt.get_edge_data(a, b).get('tokens', 0)
                min_tokens = apx_pess.get_edge_data(a, b).get('tokens', 0)
                if (max_tokens > min_tokens):
                    print(
                        "Channel ({}, {}): production = {}, consumption = {}, token-diff = {}, vectorising consumer"
                        .format(a, b, prates, crates, max_tokens - min_tokens))
                    r, v = retime_vectorise(g, a, b)
                    if r > 0:
                        print("Retiming {} by simulating {} firings".format(
                            b, r))

                    print("Vectorising {} by vector {}...".format(b, v),
                          end="")
                    blocking_vectors[b] = a, r, v
                    g = parallelise(g, {b: v})
                    print("done")
                    break
            else:
                # approximation is exact
                break

    return g
Esempio n. 48
0
def unfold(sdfg, dct=None, **periods):
    if dct is not None:
        periods.update(dct)

    result = core.SDFGraph()
    for v, data in sdfg.nodes_iter(data=True):
        Tv = periods.get(v, 1)
        for i in range(Tv):
            result.add_node((v, i + 1) if Tv > 1 else v,
                            wcet=data.get('wcet', core.cyclic(0))[i::Tv])

    for v, w, data in sdfg.edges_iter(data=True):
        Tv = periods.get(v, 1)
        Tw = periods.get(w, 1)
        prates = data.get('production', core.cyclic(1))
        crates = data.get('consumption', core.cyclic(1))
        tokens = data.get('tokens', 0)
        plen = len(prates)

        # consuming actor in unfolded graph has a single phase
        # and consumption rate equal to periods_w * crates.sum()
        csum = crates.sum() * Tw // gcd(Tw, len(crates))

        # sum of production rates in multi-rate equivalent of unfolded graph
        psum = prates.sum() * Tv // gcd(Tv, len(prates))

        gm = gcd(csum, psum)
        gvw = gcd(csum, prates.sum())

        # multiplicative inverse of (psum // gvw) modulo (gm // gvw)
        g_cd, mulinv, _ = xgcd(prates.sum() // gvw, gm // gvw)
        assert g_cd == 1

        # iterate consuming actors
        for j in range(Tw):
            # determine which incoming channels must be added
            incoming = set()

            # see Algorithm ? in PhD thesis
            sols = set()
            for n0 in range(len(crates) // gcd(len(crates), Tw)):
                for i0 in range(plen):
                    delta_i_n = tokens + prates.sum(stop=i0 + 1) - crates.sum(
                        stop=j + 1 + n0 * Tw)
                    delta_i1_n = tokens + prates.sum(stop=i0) - crates.sum(
                        stop=j + 1 + n0 * Tw)
                    sol_min = (gvw - delta_i_n - 1) // gvw
                    sol_max = (gvw - delta_i1_n - 1) // gvw

                    for sol in range(sol_min, sol_max):
                        s = (i0 + sol * mulinv * plen) % gcd(
                            Tv, plen * gm // gvw)
                        sols.add(s)

            for i_residue in sols:
                for i0 in range(i_residue, Tv, gcd(Tv, plen * gm // gvw)):
                    incoming.add(i0)

            # compute consumption rates for incoming channels
            tokens_c, incoming_crates = sample_crates(crates, j, Tw)

            # add incoming channels:
            for i in incoming:
                # compute production rates
                tokens_p, incoming_prates = sample_prates(prates, i, Tv)

                extra_data = dict()
                if 'var' in data:
                    extra_data['var'] = data['var']

                # add channel
                vi = (v, i + 1) if Tv > 1 else v
                wj = (w, j + 1) if Tw > 1 else w
                result.add_edge(vi,
                                wj,
                                production=incoming_prates,
                                consumption=incoming_crates,
                                tokens=tokens + tokens_c + tokens_p,
                                **extra_data)

    return result
Esempio n. 49
0
def unfold( sdfg, dct = None, **periods ):
    if dct is not None:
        periods.update( dct )

    result = core.SDFGraph()
    for v, data in sdfg.nodes_iter( data = True ):
        Tv = periods.get(v, 1)
        for i in range(Tv):
            result.add_node( (v, i + 1) if Tv > 1 else v, wcet = data.get('wcet', core.cyclic(0))[i::Tv])

    for v, w, data in sdfg.edges_iter( data = True ):
        Tv = periods.get(v, 1)
        Tw = periods.get(w, 1)
        prates = data.get('production', core.cyclic(1))
        crates = data.get('consumption', core.cyclic(1))
        tokens = data.get('tokens', 0)
        plen = len(prates)

        # consuming actor in unfolded graph has a single phase
        # and consumption rate equal to periods_w * crates.sum()
        csum = crates.sum() * Tw // gcd(Tw, len( crates ))

        # sum of production rates in multi-rate equivalent of unfolded graph
        psum = prates.sum() * Tv // gcd(Tv, len( prates ))

        gm = gcd( csum, psum )
        gvw = gcd( csum, prates.sum() )

        # multiplicative inverse of (psum // gvw) modulo (gm // gvw)
        g_cd, mulinv, _ = xgcd( prates.sum() // gvw, gm // gvw )
        assert g_cd == 1

        # iterate consuming actors
        for j in range(Tw):
            # determine which incoming channels must be added
            incoming = set()

            # see Algorithm ? in PhD thesis
            sols = set()
            for n0 in range( len( crates ) // gcd( len( crates ), Tw )):
                for i0 in range( plen ):
                    delta_i_n = tokens + prates.sum(stop = i0 + 1) - crates.sum(stop = j + 1 + n0 * Tw )
                    delta_i1_n = tokens + prates.sum(stop = i0) - crates.sum(stop = j + 1 + n0 * Tw )
                    sol_min = (gvw - delta_i_n - 1) // gvw
                    sol_max = (gvw - delta_i1_n - 1) // gvw

                    for sol in range(sol_min, sol_max):
                        s = (i0 + sol * mulinv * plen) % gcd(Tv, plen * gm // gvw)
                        sols.add( s )

            for i_residue in sols:
                for i0 in range(i_residue, Tv, gcd(Tv, plen * gm // gvw)):
                    incoming.add( i0 )

            # compute consumption rates for incoming channels
            tokens_c, incoming_crates = sample_crates( crates, j, Tw )

            # add incoming channels:
            for i in incoming:
                # compute production rates
                tokens_p, incoming_prates = sample_prates( prates, i, Tv )

                extra_data = dict()
                if 'var' in data:
                    extra_data['var'] = data['var']

                # add channel
                vi = (v, i + 1) if Tv > 1 else v
                wj = (w, j + 1) if Tw > 1 else w
                result.add_edge( vi, wj,
                    production = incoming_prates,
                    consumption = incoming_crates,
                    tokens = tokens + tokens_c + tokens_p,
                    **extra_data)

    return result