Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

#!/usr/bin/env python

"""Plot the performance for different block sizes of one function across
variants.
"""

import libplot

import pylab
import pdb
import math

def pretty_kb(v):
    if v < 1024:
        return '%d' % v
    else:
        if v % 1024 == 0:
            return '%d k' % (v//1024)
        else:
            return '%.1f k' % (v/1024)

def plot(records, function, alignment=None, scale=1):
    variants = libplot.unique(records, 'variant', prefer='this')
    records = [x for x in records if x.function==function]

    if alignment != None:
        records = [x for x in records if x.src_alignment==alignment[0] and
                   x.dst_alignment==alignment[1]]

    alignments = libplot.unique(records, ('src_alignment', 'dst_alignment'))
    if len(alignments) != 1:
        return False
    if libplot.alignments_equal(alignments):
        aalignment = alignments[0][0]
    else:
        aalignment = "%s:%s" % (alignments[0][0], alignments[0][1])

    bytes = libplot.unique(records, 'bytes')[0]

    colours = libplot.make_colours()
    all_x = []

    pylab.figure(1).set_size_inches((6.4*scale, 4.8*scale))
    pylab.clf()

    if 'str' in function:
        # The harness fills out to 16k.  Anything past that is an
        # early match
        top = 16384
    else:
        top = 2**31

    for variant in variants:
        matches = [x for x in records if x.variant==variant and x.bytes <= top]
        matches.sort(key=lambda x: x.bytes)

        X = sorted(list(set([x.bytes for x in matches])))
        Y = []
        Yerr = []
        for xbytes in X:
            vals = [x.bytes*x.loops/x.elapsed/(1024*1024) for x in matches if x.bytes == xbytes]
            if len(vals) > 1:
                mean = sum(vals)/len(vals)
                Y.append(mean)
                if len(Yerr) == 0:
                    Yerr = [[], []]
                err1 = max(vals) - mean
                assert err1 >= 0
                err2 = min(vals) - mean
                assert err2 <= 0
                Yerr[0].append(abs(err2))
                Yerr[1].append(err1)
            else:
                Y.append(vals[0])

        all_x.extend(X)
        colour = colours.next()

        if X:
            pylab.plot(X, Y, c=colour)
            if len(Yerr) > 0:
                pylab.errorbar(X, Y, yerr=Yerr, c=colour, label=variant, fmt='o')
            else:
                pylab.scatter(X, Y, c=colour, label=variant, edgecolors='none')

    pylab.legend(loc='upper left', ncol=3, prop={'size': 'small'})
    pylab.grid()
    pylab.title('%(function)s of %(aalignment)s byte aligned blocks' % locals())
    pylab.xlabel('Size (B)')
    pylab.ylabel('Rate (MB/s)')

    # Figure out how high the range goes
    top = max(all_x)

    power = int(round(math.log(max(all_x)) / math.log(2)))

    pylab.semilogx()

    pylab.axes().set_xticks([2**x for x in range(0, power+1)])
    pylab.axes().set_xticklabels([pretty_kb(2**x) for x in range(0, power+1)])
    pylab.xlim(0, top)
    pylab.ylim(0, pylab.ylim()[1])
    return True

def main():
    records = libplot.parse()

    functions = libplot.unique(records, 'function')
    alignments = libplot.unique(records, ('src_alignment', 'dst_alignment'))

    for function in functions:
        for alignment in alignments:
            for scale in [1, 2.5]:
                if plot(records, function, alignment, scale):
                    pylab.savefig('sizes-%s-%02d-%02d-%.1f.png' % (function, alignment[0], alignment[1], scale), dpi=72)

    pylab.show()

if __name__ == '__main__':
    main()