- PVSM.RU - https://www.pvsm.ru -
Данная статья не является ни документацией, ни рассказывает что-то принципиально новое, её стоит рассматривать как обзорную или как шпаргалку.
Конференция это прежде всего доклады, и далеко не последнее место занимает то, как оформлены слайды доклада.
Безусловно, есть докладчики [1], которые могут не смотря ни на что, провести доклад даже без единого слайда, но всё же они как правило хорошо дополняют повествование. Одним [2] достаточно накидать мемасиков в доклад и дело готово, другим [3] обязательно надо вставить код, причём на ассемблере (кто не в курсе ещё — JPoint [4] — это конференция по java), и есть ещё те, кому надо показать графики. Впрочем встречается и их комбинация.
Пожалуй известные средства для создания слайдов это:
И если первые два по своей сути представляют бинарные форматы, а облачные Google Slides и ко [5] ещё требуют наличие интернета, что является некоторым неприятным ограничением (во время поездок и перелётов), то последние два — и оффлайновые, и исключительно текстовые, а значит можно хранить историю всех изменений в git/hg/на ваш вкус. Кроме того, область применения далеко не ограничена только слайдами.
LaTeX формат с историей — много написано и сказано, а вот RMarkdown молодой, даже немного хипстерский, но без подворотов.
Markdown это облегченный язык разметки, созданный с целью написания максимально читаемого и удобного для правки текста. Markdown является и лёгким для понимания, и легким для чтения даже без каких-либо трансформаций.
Сравните сами: _курсив_
это курсив, **сильное выделение**
это сильное выделение, и многое другое — более подробно описано в Markdown cheatsheet [8].
Markdown поддерживают github, habrahabr, sublime, jira (имеет схожий синтаксис), и многие другие.
R [9] — язык программирования для статистической обработки данных и работы с графикой.
Как правило это останавливает — это очень сложно, это математика, и это не надо — но никто же заставляет использовать всю доступную функциональность, пожалуй самый простой и наглядный — это графики и визуализация.
Хотя часто для того, чтобы построить какой-либо график используют Excel, он с большим трудом справляется, когда количество данных приближается уже к миллиону. Тогда как для R это не является какой-то сложной задачей.
Оставим за кадром баталию, что же лучше — таблицы или графики. Вопрос вкуса.
Для построения графиков используем расширение ggplot2 [10].
Устанавливаем модуль для R :
install.packages("ggplot2")
Но чтобы строить графики нужны данные, и разумно хранить их отдельно от представления, например, в csv формате — опять же — простой текстовый формат.
Мои данные — это результаты полученные при помощи jmh [11] для моего доклада Внутрь VM сквозь замочную скважину hashCode [12]. Мне нравится стиль, используемый Алексеем Шипилёвым: записывать результаты benchmark'а в виде комментария в начале файла — grep-n-sed и мы имеем csv-файл.
данные csv/allocations.csv
:
pos,alloc,value,error
10,single-threaded,2.836,0.285
20,java,9.878,2.676
28,epsilon,75.289,23.667
30,sync,186.672,21.195
40,cas,74.721,0.192
50,tlab,8.506,1.849
55,javaHashCode,60.270,12.318
57,readHashCode,7.296,0.316
Формируем таблицу данных (data frame) из csv-файла — по-умолчанию, считается, что в файле есть заголовок — это нам поможет при обращении к отдельным колонкам
```{r}
df = read.csv(file = "csv/allocations.csv")
```
если хотим отфильтровать, н-р, по конкретным значениям в колонке alloc
df <- subset(df, alloc == "cas" | alloc == "java" | alloc == "sync" | alloc == "tlab" )
Для начала необходимо задать схему соответствия переменных (aes) из таблицы данных — отображать будем значение от типа, в нашем случае типа аллокации, цвет бара будет выбран также на основе типа аллокации.
ggplot(data=df, aes(x=alloc, y=value, fill=alloc))
Отображать будем в виде столбиков ( bar chart ) + geom_bar()
ggplot(data=df, aes(x=alloc, y=value, fill=alloc))
+ geom_bar(stat="identity")
результат:
повернём систему координат (из вертикальных в горизонтальные бары) опцией + coord_flip()
ggplot(data=df, aes(x=alloc, y=value, fill=alloc)) +
geom_bar(stat="identity") +
coord_flip()
добавим ошибку изменений + geom_errorbar() (помните, у нас в csv-файле есть колонка error):
ggplot(data=df, aes(x=alloc, y=value, fill=alloc)) +
geom_bar(stat="identity") +
coord_flip() +
geom_errorbar(aes(ymin = value - error, ymax = value + error), width=0.5, alpha=0.5)
для наглядности стоит добавить значения рядом с баром + geom_text() (логично, что текстом будет value)
ggplot(data=df, aes(x=alloc, y=value, fill=alloc)) +
geom_bar(stat="identity") +
coord_flip() +
geom_errorbar(aes(ymin = value - error, ymax = value + error), width=0.5, alpha=0.5) +
geom_text(aes(label=value))
добавим лоска подписи + geom_text():
изменим размер шрифта size и начертание fontface подписи
ggplot(data=df, aes(x=alloc, y=value, fill=alloc)) +
geom_bar(stat="identity") +
coord_flip() +
geom_errorbar(aes(ymin = value — error, ymax = value + error), width=0.5, alpha=0.5) +
geom_text(aes(label=base::sprintf("%0.2f ± %0.2f", value, error)), hjust=-0.1, vjust=-0.4, size=5, fontface = "bold")
добавим подписи + labs(title =…, x =…, y = ..) и шрифты для осей + theme(axis.text.y = ..)
ggplot(data=df, aes(x=alloc, y=value, fill=alloc)) +
geom_bar(stat="identity") +
coord_flip() +
geom_errorbar(aes(ymin = value — error, ymax = value + error), width=0.5, alpha=0.5) +
geom_text(aes(label=base::sprintf("%0.2f ± %0.2f", value, error)), hjust=-0.1, vjust=-0.4, size=5, fontface = "bold") +
labs(title = "@Threads( 4 )", x = "", y = "ns/op") +
theme_classic() +
theme(axis.text.y = element_text(size = 16, face = "bold")) +
theme(axis.title = element_text(size = 16, face = "bold")) +
theme(legend.position="none")
и для придания финального лоска
ggplot(data=df, aes(x=reorder(alloc, -pos), y=value, fill=alloc)) +
geom_bar(stat="identity") +
coord_flip() +
geom_errorbar(aes(ymin = value - error, ymax = value + error), width=0.5, alpha=0.5) +
geom_text(aes(label=base::sprintf("%0.2f ± %0.2f", value, error)), hjust=-0.1, vjust=-0.4, size=5, fontface = "bold") +
scale_fill_manual(values=c(java'='#a9a518','sync'='#fa8074', 'cas'='#00b3f6', 'tlab'='#e67bf3')) +
labs(title = "@Threads( 4 )", x = "", y = "ns/op") +
theme_classic() +
scale_y_continuous(limits=c(0, max(df$value) + 40), expand = c(0, 0)) +
theme(axis.text.y = element_text(size = 16, face = "bold")) +
theme(axis.title = element_text(size = 16, face = "bold")) +
theme(legend.position="none")
tip: можно сохранить график в файл + ggsave("allocations.svg"), но не стоит злоупотреблять векторным форматом, если на графике много точек — сохраняйте в растр, н-р png.
install.packages("svglite")
Отдельно стоит отметить цветовую палитру, используемую по-умолчанию: выбираемые цвета хорошо различимы даже для людей с ослабленым цветовосприятием.
Не используйте сочетание красный/зелёный даже если вы используете всего два цвета, чтобы выделить, что лучше/хуже — эти цвета слабо различимы для людей с ослабленым цветовосприятием.
tip: colorbrewer2 [13] поможет выбрать в т.ч. безопасные цвета.
Есть некоторое распределение адресов по нитям
step,thread,address
1,thread-0,807437816
1,thread-1,807437784
1,thread-2,807437800
..........
отобразим отдельно по точкам + geom_point
ggplot(data=df, aes(x = address, y = index, group=thread, colour=thread, shape=thread)) +
geom_point(size=2)
Первое, что бросается в глаза (кроме того, что много данных) — это значения адресов в научной нотации. Как-то привычней иметь дело с адресами в 16-тиричной системе: добавим форматирование значений по оси X +scale_x_continuous():
ggplot(data=df, aes(x = address, y = index, group=thread, colour=thread, shape=thread)) +
geom_point(size=2) +
scale_x_continuous(
labels = function(n){base::sprintf("0x%X", as.integer(n))}
)
Лучше, но всё равно как-то сложно и мало понятно.
Поскольку мы можем задавать любую функцию, то почему бы не отображать смещение относительно некоторого базового, н-р минимального, адреса:
min_address = min(df$address)
ggplot(data=df, aes(x = address, y = index, group=thread, colour=thread, shape=thread)) +
geom_point(size=2) +
scale_x_continuous(
labels = function(n){base::sprintf("+ %d MiB", as.integer((n - min_address)/1024/1024))}
)
Опять же в силу привычки — поставим метки (breaks =) по целым числам — а именно 16, 32, 48, 64Мб:
ggplot(data=df, aes(x = address, y = index, group=thread, colour=thread, shape=thread)) +
geom_point(size=2) +
scale_x_continuous(
labels = function(n){ifelse(n == min_address, base::sprintf("base"), base::sprintf("+ %d MiB", as.integer((n - min_address)/1024/1024)))},
breaks=c(min_address, min_address + 16*1024*1024, min_address + 32*1024*1024, min_address + 48*1024*1024, min_address + 64*1024*1024)
)
Данных много — хотим взглянуть на небольшую её часть
добавим метки в нужных местах на оси Y: + scale_y_continuous(breaks = c(...))
df = read.csv(file = "csv/hashCodesNoTLAB.csv", nrows = 36, header = TRUE)
min_address = min(df$address)
ggplot(data=df, aes(x = address, y = index, group=thread, colour=thread, shape=thread)) +
geom_point(size=4) +
scale_x_continuous(
labels = function(n){ifelse(n == min_address, base::sprintf("base"), base::sprintf("+ %d", as.integer((n — min_address))))},
breaks=c(min_address, min_address + 1610, min_address + 21610, min_address + 31610, min_address + 41610, min_address + 516*10)
) +
scale_y_continuous( breaks = c(1,2,3,4,5,6,7,8,9,10) ) +
И конечно же — гистограмма [15], частотное распределение. Очень грубо это можно описать как количество элементов попадающих в диапазон значений. Н-р, для ряда [1, 2, 3, 1, 1] — гистограмма будет выглядеть как [3, 1, 1] — т.к элемент 1 встретился 3 раза, а элементы 2 и 3 по одному разу.
addressHashCode = read.csv(file = "csv/addressHashCode.csv")
defaultHashCode = read.csv(file = "csv/defaultHashCode.csv")
ggplot() +
geom_histogram(data=addressHashCode, aes(x=hashCode, fill="address"), alpha=0.7, bins = 500) +
geom_histogram(data=defaultHashCode, aes(x=hashCode, fill="MXSRng"), alpha=0.7, bins = 500) +
Добавляем уже известные опции до придания нужного вида:
ggplot() +
geom_histogram(data=addressHashCode, aes(x=hashCode, fill="address"), alpha=0.7, bins = 500) +
geom_histogram(data=defaultHashCode, aes(x=hashCode, fill="MXSRng"), alpha=0.7, bins = 500) +
scale_fill_manual(name="тип hashCode:", labels=c("address"="адрес", "MXSRng"="MXS-гпсч"), values=c("address" ="#003dae", "MXSRng" = "#ae003d")) +
labs(title =
sprintf("количество коллизий по адресу: %s k, по MXS-гпсч: %s k",
round( sum(duplicated(addressHashCode)) / 1000, 1),
round( sum(duplicated(defaultHashCode)) / 1000, 1)),
x = "hashCode") +
theme_classic() +
theme(axis.title.y=element_blank()) +
scale_y_continuous(labels = function(n){format(n, big.mark = "_", scientific = FALSE)}, expand = c(0, 0)) +
scale_x_continuous(labels = function(n){format(n, big.mark = "_", scientific = FALSE)}, expand = c(0, 0)) +
theme(axis.title = element_text(size = 16, face = "bold")) +
theme(axis.text.y = element_text(size = 14, face = "bold")) +
theme(axis.text.x = element_text(size = 14, face = "bold")) +
theme(axis.title = element_text(size = 16, face = "bold")) +
theme(axis.title.x=element_text(margin=margin(t=20))) +
theme(legend.text = element_text(size = 14, face = "bold")) +
theme(title = element_text(size = 16, face = "bold")) +
А пони [16]? Запросто!
install.packages('png')
# install.packages('png')
img <- readPNG("images/pony.png")
g <- rasterGrob(img, interpolate=TRUE, x = 0.1, y = 0.9, width = 0.2, height = 0.2)
ggplot() +
annotation_custom(g, xmin=-Inf, ymin = -Inf, xmax=Inf, ymax=Inf) +
geom_histogram(data=addressHashCode, aes(x=hashCode, fill="address"), alpha=0.7, bins = 500) +
geom_histogram(data=defaultHashCode, aes(x=hashCode, fill="MXSRng"), alpha=0.7, bins = 500)
Немного полезных ссылок: ggplot2 tutorial [17] и ещё один полезный блог Data wrangling, exploration, and analysis with R [18]
Не надо быть большим капитаном, чтобы сообразить, что RMarkdown [7] это R + Markdown.
Устанавливаем модуль для R :
install.packages("rmarkdown")
И со словами HTML знаю, LaTeX люблю рендерим RMarkdown:
rmarkdown::render("path_to_file.Rmd", encoding = "UTF-8")
В заголовке досаточно указать, например:
output: pdf_document
для этого в заголовке достаточно поменять output:
output: ioslides_presentation
Автор: Владимир Долженко
Источник [24]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/java/254339
Ссылки в тексте:
[1] докладчики: https://twitter.com/shipilev
[2] Одним: https://twitter.com/jbaruch
[3] другим: https://twitter.com/iwan0www
[4] JPoint: https://jpoint.ru/
[5] Google Slides: https://www.google.com/slides
[6] LaTeX: https://www.latex-project.org/
[7] RMarkdown: http://rmarkdown.rstudio.com/
[8] Markdown cheatsheet: https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet
[9] R: https://www.r-project.org/
[10] ggplot2: http://ggplot2.tidyverse.org/index.html
[11] jmh: http://openjdk.java.net/projects/code-tools/jmh
[12] Внутрь VM сквозь замочную скважину hashCode: https://jpoint.ru/talks/through-the-keyhole-of-hashcode-into-vm/
[13] colorbrewer2: http://colorbrewer2.org/#type=diverging&scheme=RdBu&n=6
[14] формы маркера: http://www.cookbook-r.com/Graphs/Shapes_and_line_types/
[15] гистограмма: https://ru.wikipedia.org/wiki/Гистограмма
[16] А пони: https://twitter.com/shipilev/status/857311629847015425
[17] ggplot2 tutorial: https://github.com/jennybc/ggplot2-tutorial
[18] Data wrangling, exploration, and analysis with R: http://stat545.com/topics.html
[19] html: http://rmarkdown.rstudio.com/html_document_format.html
[20] pdf: http://rmarkdown.rstudio.com/pdf_document_format.html
[21] doc: http://rmarkdown.rstudio.com/word_document_format.html
[22] HTML5: http://rmarkdown.rstudio.com/ioslides_presentation_format.html
[23] PDF: http://rmarkdown.rstudio.com/beamer_presentation_format.html
[24] Источник: https://habrahabr.ru/post/327912/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.