Add 12th lab

master
Yury Kurlykov 2020-06-18 13:19:27 +10:00
parent 1243883cec
commit bd10ce553a
Signed by: t1meshift
GPG Key ID: B133F3167ABF94D8
9 changed files with 699 additions and 1 deletions

View File

@ -21,4 +21,5 @@ define_lab(lab6)
define_lab(lab7)
define_lab(lab8)
define_lab(lab9)
define_lab(lab10)
define_lab(lab10)
define_lab(lab12)

View File

@ -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)
## Запуск

10
lab12/.execme Executable file
View File

@ -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

36
lab12/CMakeLists.txt Normal file
View File

@ -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 ()

53
lab12/README.md Normal file
View File

@ -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) Б).

72
lab12/cache.c Normal file
View File

@ -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 <stdio.h>
#include <unistd.h>
#include <sys/times.h>
#include <sys/types.h>
#include <time.h>
#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<steps); /* until = no. iterations */
printf("Size: %7ld Stride: %7ld read+write: %4.4lf ns\n",
csize*sizeof(int), stride*sizeof(int),
(double) sec*1e9/(steps*SAMPLE*stride*((limit-1)/stride+1)));
}; /* end of both outer for loops */
}

BIN
lab12/g1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

20
lab12/graph_data.py Normal file
View File

@ -0,0 +1,20 @@
import thinkplot
import matplotlib.pyplot as pyplot
d = {}
for line in open('data'):
t = line.split()
size, stride, time = int(t[1]), int(t[3]), float(t[5])
d.setdefault(stride, []).append((size, time))
thinkplot.PrePlot(num=7)
for stride in sorted(d.keys()):
if stride >= 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)')

504
lab12/thinkplot.py Normal file
View File

@ -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()