diff --git a/CMakeLists.txt b/CMakeLists.txt index e8f7be2..39330da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,4 +21,5 @@ define_lab(lab6) define_lab(lab7) define_lab(lab8) define_lab(lab9) -define_lab(lab10) \ No newline at end of file +define_lab(lab10) +define_lab(lab12) \ No newline at end of file diff --git a/README.md b/README.md index 9a22440..70a150e 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,11 @@ - [Лабораторная работа 5](lab5/README.md) - [Лабораторная работа 6](lab6/README.md) - [Лабораторная работа 7](lab7/README.md) +- [Лабораторная работа 8](lab8/README.md) - [Лабораторная работа 9](lab9/README.md) - [Лабораторная работа 10](lab10/README.md) - [Лабораторная работа 11](lab11/README.md) +- [Лабораторная работа 12](lab12/README.md) ## Запуск diff --git a/lab12/.execme b/lab12/.execme new file mode 100755 index 0000000..dad8c12 --- /dev/null +++ b/lab12/.execme @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -euo pipefail +IFS=$'\n\t' + +pushd "$1" > /dev/null + +./lab12_cache.c_run > ./data && lscpu && cat /proc/cpuinfo && python2 ./graph_data.py + +popd > /dev/null \ No newline at end of file diff --git a/lab12/CMakeLists.txt b/lab12/CMakeLists.txt new file mode 100644 index 0000000..fd72e9a --- /dev/null +++ b/lab12/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.16) +set(CMAKE_C_STANDARD 11) + +# Lab name +set(LAB_NAME "lab12") + +# Lab tasks +list(APPEND SOURCE_FILES + cache.c + ) +list(APPEND NON_COMPILABLE_SRC + .execme + graph_data.py + thinkplot.py + ) + +### Here goes the template + +project("${LAB_NAME}" C) + +add_custom_target("${LAB_NAME}") + +foreach (file IN LISTS SOURCE_FILES) + add_executable("${LAB_NAME}_${file}_run" "${file}") + add_dependencies("${LAB_NAME}" "${LAB_NAME}_${file}_run") +endforeach () + +foreach (file IN LISTS NON_COMPILABLE_SRC) + add_custom_command( + TARGET "${LAB_NAME}" POST_BUILD + DEPENDS "${file}" + COMMAND ${CMAKE_COMMAND} -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/${file}" + "${CMAKE_CURRENT_BINARY_DIR}/${file}" + ) +endforeach () \ No newline at end of file diff --git a/lab12/README.md b/lab12/README.md new file mode 100644 index 0000000..44c278f --- /dev/null +++ b/lab12/README.md @@ -0,0 +1,53 @@ +# Лабораторная работа №12 + +> Проанализируйте cache.c и с ее использованием исследуйте параметры кэша на вашем компьютере. Для этого +> 1. постройте графики времени доступа как функции длины массива, шага выборки и размера буфера. +> 2. на их основе сформулируйте обоснованные гипотезы о размере кэша, размере блока, наличию кэша более высокого уровня. +> 3. сравните свои оценки с реальными значениями, полученными через вызов системных функций или из технического описания вашего компьютера. + +График: + +![](g1.png) + +Как видно из графика, стремительный рост access time происходит на 2^22 B, что примерно равно 4 мегабайтам. +Из этого можно предположить, что размер кэша -- 4Мб. На размере блока выше 64 байт происходит увеличение access time, +что может быть связано с тем, что физический размер блока -- 64 байта. Также наблюдаются ускорения при размере 2^20 и +2^21, что может говорить о существовании некоторых кэшей размером в 1 и 2 Мб. + +Вывод `cat /proc/cpuinfo`: +```text +... +cache size : 3072 KB +bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds swapgs itlb_multihit srbds +bogomips : 3792.26 +clflush size : 64 +cache_alignment : 64 +address sizes : 39 bits physical, 48 bits virtual +... +``` + +Вывод `lscpu`: +```text +... +L1d cache: 64 KiB +L1i cache: 64 KiB +L2 cache: 512 KiB +L3 cache: 3 MiB +Vulnerability Itlb multihit: KVM: Mitigation: Split huge pages +Vulnerability L1tf: Mitigation; PTE Inversion; VMX conditional cache flushes, SMT vulnerable +Vulnerability Mds: Mitigation; Clear CPU buffers; SMT vulnerable +Vulnerability Meltdown: Mitigation; PTI +Vulnerability Spec store bypass: Mitigation; Speculative Store Bypass disabled via prctl and seccomp +Vulnerability Spectre v1: Mitigation; usercopy/swapgs barriers and __user pointer sanitization +Vulnerability Spectre v2: Mitigation; Full generic retpoline, IBPB conditional, IBRS_FW, STIBP conditional, RSB filling +Vulnerability Srbds: Mitigation; Microcode +Vulnerability Tsx async abort: Not affected +... +``` + +Исходя из этих данных, можно предположить, что в связи с патчами для устранения уязвимостей процессора +(Spectre, Meltdown, L1TF и прочие) график может не вполне корректно отражать реальное положение дел. + +Но выводы оказались достаточно приближены к действительности: мы видим два L1-кэша размера 64 Кб (не видно +на графике, т.к. 2^16 Б меньше левой границы графика), L2-кэш размера 512 Кб (2^19 Б) и L3-кэш размера 3 Мб +(~2^(21.6) Б). diff --git a/lab12/cache.c b/lab12/cache.c new file mode 100644 index 0000000..fc630ed --- /dev/null +++ b/lab12/cache.c @@ -0,0 +1,72 @@ +/****************************************************************** + * CACHE project * + * * + * Using this program, on as many different kinds of computers as * + * possible, investigate these cache parameters: * + * -- total cache size * + * -- cache width * + * -- cache replacement policy * + ******************************************************************/ + +/* I got this program from Brian Harvey, who teaches CS61C at + Berkeley. He didn't put a copyright on it, but he should + at least get credit for it. Thanks, Brian! */ + +#include +#include +#include +#include +#include + +#define CACHE_MIN (32*1024) /* smallest cache */ +#define CACHE_MAX (32*1024*1024) /* largest cache */ +#define SAMPLE 10 /* to get a larger time sample */ + +int x[CACHE_MAX]; /* array going to stride through */ +long clk_tck; + +double get_seconds() { /* routine to read time */ + struct tms rusage; + times(&rusage); /* UNIX utility: time in clock ticks */ + return (double) (rusage.tms_utime)/clk_tck; +} + +int main() { + int register i, index, stride, limit, temp; + int steps, tsteps, csize; + double sec0, sec; /* timing variables */ + + clk_tck = sysconf(_SC_CLK_TCK); + + for (csize=CACHE_MIN; csize <= CACHE_MAX; csize=csize*2) + for (stride=1; stride <= csize/2; stride=stride*2) { + sec = 0; /* initialize timer */ + limit = csize-stride+1; /* cache size this loop */ + + steps = 0; + do { /* repeat until collect 1 second */ + sec0 = get_seconds(); /* start timer */ + for (i=SAMPLE*stride;i!=0;i=i-1) /* larger sample */ + for (index=0; index < limit; index=index+stride) + x[index] = x[index] + 1; /* cache access */ + steps = steps + 1; /* count while loop iterations */ + sec = sec + (get_seconds() - sec0);/* end timer */ + } while (sec < 1.0); /* until collect 1 second */ + + /* Repeat empty loop to loop subtract overhead */ + tsteps = 0; /* used to match no. while iterations */ + do { /* repeat until same no. iterations as above */ + sec0 = get_seconds(); /* start timer */ + for (i=SAMPLE*stride;i!=0;i=i-1) /* larger sample */ + for (index=0; index < limit; index=index+stride) + temp = temp + index; /* dummy code */ + tsteps = tsteps + 1; /* count while iterations */ + sec = sec - (get_seconds() - sec0);/* - overhead */ + } while (tsteps= 512: continue + + xs, ys = zip(*d[stride]) + thinkplot.plot(xs, ys, label=str(stride)) + print stride, len(d[stride]) + +pyplot.xscale('log', basex=2) +thinkplot.show(xlabel='size (B)', ylabel='access time (ns)') diff --git a/lab12/thinkplot.py b/lab12/thinkplot.py new file mode 100644 index 0000000..ed01a2c --- /dev/null +++ b/lab12/thinkplot.py @@ -0,0 +1,504 @@ +"""This file contains code for use with "Think Stats", +by Allen B. Downey, available from greenteapress.com + +Copyright 2010 Allen B. Downey +License: GNU GPLv3 http://www.gnu.org/licenses/gpl.html +""" + +import math +import matplotlib +import matplotlib.pyplot as pyplot +import numpy as np + +# customize some matplotlib attributes +#matplotlib.rc('figure', figsize=(4, 3)) + +#matplotlib.rc('font', size=14.0) +#matplotlib.rc('axes', labelsize=22.0, titlesize=22.0) +#matplotlib.rc('legend', fontsize=20.0) + +#matplotlib.rc('xtick.major', size=6.0) +#matplotlib.rc('xtick.minor', size=3.0) + +#matplotlib.rc('ytick.major', size=6.0) +#matplotlib.rc('ytick.minor', size=3.0) + + +class Brewer(object): + """Encapsulates a nice sequence of colors. + + Shades of blue that look good in color and can be distinguished + in grayscale (up to a point). + + Borrowed from http://colorbrewer2.org/ + """ + color_iter = None + + colors = ['#081D58', + '#253494', + '#225EA8', + '#1D91C0', + '#41B6C4', + '#7FCDBB', + '#C7E9B4', + '#EDF8B1', + '#FFFFD9'] + + # lists that indicate which colors to use depending on how many are used + which_colors = [[], + [1], + [1, 3], + [0, 2, 4], + [0, 2, 4, 6], + [0, 2, 3, 5, 6], + [0, 2, 3, 4, 5, 6], + [0, 1, 2, 3, 4, 5, 6], + ] + + @classmethod + def Colors(cls): + """Returns the list of colors. + """ + return cls.colors + + @classmethod + def ColorGenerator(cls, n): + """Returns an iterator of color strings. + + n: how many colors will be used + """ + for i in cls.which_colors[n]: + yield cls.colors[i] + raise StopIteration('Ran out of colors in Brewer.ColorGenerator') + + @classmethod + def InitializeIter(cls, num): + """Initializes the color iterator with the given number of colors.""" + cls.color_iter = cls.ColorGenerator(num) + + @classmethod + def ClearIter(cls): + """Sets the color iterator to None.""" + cls.color_iter = None + + @classmethod + def GetIter(cls): + """Gets the color iterator.""" + return cls.color_iter + + +def PrePlot(num=None, rows=1, cols=1): + """Takes hints about what's coming. + + num: number of lines that will be plotted + """ + if num: + Brewer.InitializeIter(num) + + # TODO: get sharey and sharex working. probably means switching + # to subplots instead of subplot. + # also, get rid of the gray background. + + if rows > 1 or cols > 1: + pyplot.subplots(rows, cols, sharey=True) + global SUBPLOT_ROWS, SUBPLOT_COLS + SUBPLOT_ROWS = rows + SUBPLOT_COLS = cols + + +def SubPlot(rows, cols, plot_number): + """Configures the number of subplots and changes the current plot. + + rows: int + cols: int + plot_number: int + """ + pyplot.subplot(rows, cols, plot_number) + + +class InfiniteList(list): + """A list that returns the same value for all indices.""" + def __init__(self, val): + """Initializes the list. + + val: value to be stored + """ + list.__init__(self) + self.val = val + + def __getitem__(self, index): + """Gets the item with the given index. + + index: int + + returns: the stored value + """ + return self.val + + +def Underride(d, **options): + """Add key-value pairs to d only if key is not in d. + + If d is None, create a new dictionary. + + d: dictionary + options: keyword args to add to d + """ + if d is None: + d = {} + + for key, val in options.iteritems(): + d.setdefault(key, val) + + return d + + +def Clf(): + """Clears the figure and any hints that have been set.""" + Brewer.ClearIter() + pyplot.clf() + + +def Figure(**options): + """Sets options for the current figure.""" + Underride(options, figsize=(6, 8)) + pyplot.figure(**options) + + +def Plot(xs, ys, style='', **options): + """Plots a line. + + Args: + xs: sequence of x values + ys: sequence of y values + style: style string passed along to pyplot.plot + options: keyword args passed to pyplot.plot + """ + color_iter = Brewer.GetIter() + + if color_iter: + try: + options = Underride(options, color=color_iter.next()) + except StopIteration: + print 'Warning: Brewer ran out of colors.' + Brewer.ClearIter() + + options = Underride(options, linewidth=3, alpha=0.8) + pyplot.plot(xs, ys, style, **options) + + +def Scatter(xs, ys, **options): + """Makes a scatter plot. + + xs: x values + ys: y values + options: options passed to pyplot.scatter + """ + options = Underride(options, color='blue', alpha=0.2, + s=30, edgecolors='none') + pyplot.scatter(xs, ys, **options) + + +def Pmf(pmf, **options): + """Plots a Pmf or Hist as a line. + + Args: + pmf: Hist or Pmf object + options: keyword args passed to pyplot.plot + """ + xs, ps = pmf.Render() + if pmf.name: + options = Underride(options, label=pmf.name) + Plot(xs, ps, **options) + + +def Pmfs(pmfs, **options): + """Plots a sequence of PMFs. + + Options are passed along for all PMFs. If you want different + options for each pmf, make multiple calls to Pmf. + + Args: + pmfs: sequence of PMF objects + options: keyword args passed to pyplot.plot + """ + for pmf in pmfs: + Pmf(pmf, **options) + + +def Hist(hist, **options): + """Plots a Pmf or Hist with a bar plot. + + The default width of the bars is based on the minimum difference + between values in the Hist. If that's too small, you can override + it by providing a width keyword argument, in the same units + as the values. + + Args: + hist: Hist or Pmf object + options: keyword args passed to pyplot.bar + """ + # find the minimum distance between adjacent values + xs, fs = hist.Render() + width = min(Diff(xs)) + + if hist.name: + options = Underride(options, label=hist.name) + + options = Underride(options, + align='center', + linewidth=0, + width=width) + + pyplot.bar(xs, fs, **options) + + +def Hists(hists, **options): + """Plots two histograms as interleaved bar plots. + + Options are passed along for all PMFs. If you want different + options for each pmf, make multiple calls to Pmf. + + Args: + hists: list of two Hist or Pmf objects + options: keyword args passed to pyplot.plot + """ + for hist in hists: + Hist(hist, **options) + + +def Diff(t): + """Compute the differences between adjacent elements in a sequence. + + Args: + t: sequence of number + + Returns: + sequence of differences (length one less than t) + """ + diffs = [t[i+1] - t[i] for i in range(len(t)-1)] + return diffs + + +def Cdf(cdf, complement=False, transform=None, **options): + """Plots a CDF as a line. + + Args: + cdf: Cdf object + complement: boolean, whether to plot the complementary CDF + transform: string, one of 'exponential', 'pareto', 'weibull', 'gumbel' + options: keyword args passed to pyplot.plot + + Returns: + dictionary with the scale options that should be passed to + Config, Show or Save. + """ + xs, ps = cdf.Render() + scale = dict(xscale='linear', yscale='linear') + + for s in ['xscale', 'yscale']: + if s in options: + scale[s] = options.pop(s) + + if transform == 'exponential': + complement = True + scale['yscale'] = 'log' + + if transform == 'pareto': + complement = True + scale['yscale'] = 'log' + scale['xscale'] = 'log' + + if complement: + ps = [1.0-p for p in ps] + + if transform == 'weibull': + xs.pop() + ps.pop() + ps = [-math.log(1.0-p) for p in ps] + scale['xscale'] = 'log' + scale['yscale'] = 'log' + + if transform == 'gumbel': + xs.pop(0) + ps.pop(0) + ps = [-math.log(p) for p in ps] + scale['yscale'] = 'log' + + if cdf.name: + options = Underride(options, label=cdf.name) + + Plot(xs, ps, **options) + return scale + + +def Cdfs(cdfs, complement=False, transform=None, **options): + """Plots a sequence of CDFs. + + cdfs: sequence of CDF objects + complement: boolean, whether to plot the complementary CDF + transform: string, one of 'exponential', 'pareto', 'weibull', 'gumbel' + options: keyword args passed to pyplot.plot + """ + for cdf in cdfs: + Cdf(cdf, complement, transform, **options) + + +def Contour(obj, pcolor=False, contour=True, imshow=False, **options): + """Makes a contour plot. + + d: map from (x, y) to z, or object that provides GetDict + pcolor: boolean, whether to make a pseudocolor plot + contour: boolean, whether to make a contour plot + imshow: boolean, whether to use pyplot.imshow + options: keyword args passed to pyplot.pcolor and/or pyplot.contour + """ + try: + d = obj.GetDict() + except AttributeError: + d = obj + + Underride(options, linewidth=3, cmap=matplotlib.cm.Blues) + + xs, ys = zip(*d.iterkeys()) + xs = sorted(set(xs)) + ys = sorted(set(ys)) + + X, Y = np.meshgrid(xs, ys) + func = lambda x, y: d.get((x, y), 0) + func = np.vectorize(func) + Z = func(X, Y) + + x_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False) + axes = pyplot.gca() + axes.xaxis.set_major_formatter(x_formatter) + + if pcolor: + pyplot.pcolormesh(X, Y, Z, **options) + if contour: + cs = pyplot.contour(X, Y, Z, **options) + pyplot.clabel(cs, inline=1, fontsize=10) + if imshow: + extent = xs[0], xs[-1], ys[0], ys[-1] + pyplot.imshow(Z, extent=extent, **options) + + +def Pcolor(xs, ys, zs, pcolor=True, contour=False, **options): + """Makes a pseudocolor plot. + + xs: + ys: + zs: + pcolor: boolean, whether to make a pseudocolor plot + contour: boolean, whether to make a contour plot + options: keyword args passed to pyplot.pcolor and/or pyplot.contour + """ + Underride(options, linewidth=3, cmap=matplotlib.cm.Blues) + + X, Y = np.meshgrid(xs, ys) + Z = zs + + x_formatter = matplotlib.ticker.ScalarFormatter(useOffset=False) + axes = pyplot.gca() + axes.xaxis.set_major_formatter(x_formatter) + + if pcolor: + pyplot.pcolormesh(X, Y, Z, **options) + + if contour: + cs = pyplot.contour(X, Y, Z, **options) + pyplot.clabel(cs, inline=1, fontsize=10) + + +def Config(**options): + """Configures the plot. + + Pulls options out of the option dictionary and passes them to + the corresponding pyplot functions. + """ + names = ['title', 'xlabel', 'ylabel', 'xscale', 'yscale', + 'xticks', 'yticks', 'axis'] + + for name in names: + if name in options: + getattr(pyplot, name)(options[name]) + + loc = options.get('loc', 0) + legend = options.get('legend', True) + if legend: + pyplot.legend(loc=loc) + + +def Show(**options): + """Shows the plot. + + For options, see Config. + + options: keyword args used to invoke various pyplot functions + """ + # TODO: figure out how to show more than one plot + Config(**options) + pyplot.show() + + +def Save(root=None, formats=None, **options): + """Saves the plot in the given formats. + + For options, see Config. + + Args: + root: string filename root + formats: list of string formats + options: keyword args used to invoke various pyplot functions + """ + Config(**options) + + if formats is None: + formats = ['pdf', 'eps'] + + if root: + for fmt in formats: + SaveFormat(root, fmt) + Clf() + + +def SaveFormat(root, fmt='eps'): + """Writes the current figure to a file in the given format. + + Args: + root: string filename root + fmt: string format + """ + filename = '%s.%s' % (root, fmt) + print 'Writing', filename + pyplot.savefig(filename, format=fmt, dpi=300) + + +# provide aliases for calling functons with lower-case names +preplot = PrePlot +subplot = SubPlot +clf = Clf +figure = Figure +plot = Plot +scatter = Scatter +pmf = Pmf +pmfs = Pmfs +hist = Hist +hists = Hists +diff = Diff +cdf = Cdf +cdfs = Cdfs +contour = Contour +pcolor = Pcolor +config = Config +show = Show +save = Save + + +def main(): + color_iter = Brewer.ColorGenerator(7) + for color in color_iter: + print color + +if __name__ == '__main__': + main()