Skip to content

Instantly share code, notes, and snippets.

@ishitatsuyuki
Created September 1, 2018 13:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ishitatsuyuki/0f8acbd9e00f82820573921e8907baea to your computer and use it in GitHub Desktop.
Save ishitatsuyuki/0f8acbd9e00f82820573921e8907baea to your computer and use it in GitHub Desktop.
Grafana/InfluxDB experiment
[package]
name = "perf-influxdb"
version = "0.1.0"
authors = ["Tatsuyuki Ishi <ishitatsuyuki@gmail.com>"]
[dependencies]
serde = "1.0.75"
serde_derive = "1.0.75"
influx_db_client = "0.3.4"
serde_json = "1.0.26"
chrono = {version = "0.4.6", features = ["serde"]}
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": 1,
"iteration": 1535808279921,
"links": [],
"panels": [
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": null,
"fill": 1,
"gridPos": {
"h": 9,
"w": 24,
"x": 0,
"y": 0
},
"id": 2,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"minSpan": 8,
"nullPointMode": "null",
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"repeat": "bench",
"repeatDirection": "h",
"scopedVars": {
"bench": {
"selected": true,
"text": "cargo",
"value": "cargo"
}
},
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"groupBy": [
{
"params": [
"state"
],
"type": "tag"
}
],
"measurement": "rustc_perf",
"orderByTime": "ASC",
"policy": "default",
"refId": "A",
"resultFormat": "time_series",
"select": [
[
{
"params": [
"instructions:u"
],
"type": "field"
}
]
],
"tags": [
{
"key": "build",
"operator": "=~",
"value": "/^$build$/"
},
{
"condition": "AND",
"key": "bench",
"operator": "=~",
"value": "/^$bench$/"
}
]
}
],
"thresholds": [],
"timeFrom": null,
"timeShift": null,
"title": "$bench $build",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"refresh": false,
"schemaVersion": 16,
"style": "dark",
"tags": [],
"templating": {
"list": [
{
"allValue": null,
"current": {
"selected": true,
"text": "check",
"value": "check"
},
"datasource": "Default",
"hide": 0,
"includeAll": false,
"label": null,
"multi": false,
"name": "build",
"options": [
{
"selected": true,
"text": "check",
"value": "check"
},
{
"selected": false,
"text": "debug",
"value": "debug"
},
{
"selected": false,
"text": "release",
"value": "release"
}
],
"query": "SHOW TAG VALUES WITH KEY = build",
"refresh": 0,
"regex": "",
"sort": 0,
"tagValuesQuery": "",
"tags": [],
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"allValue": null,
"current": {
"selected": true,
"tags": [],
"text": "cargo",
"value": [
"cargo"
]
},
"datasource": "Default",
"hide": 0,
"includeAll": true,
"label": null,
"multi": true,
"name": "bench",
"options": [
{
"selected": false,
"text": "All",
"value": "$__all"
},
{
"selected": true,
"text": "cargo",
"value": "cargo"
},
{
"selected": false,
"text": "clap-rs",
"value": "clap-rs"
},
{
"selected": false,
"text": "coercions",
"value": "coercions"
},
{
"selected": false,
"text": "crates.io",
"value": "crates.io"
},
{
"selected": false,
"text": "ctfe-stress",
"value": "ctfe-stress"
},
{
"selected": false,
"text": "ctfe-stress-cast",
"value": "ctfe-stress-cast"
},
{
"selected": false,
"text": "ctfe-stress-const-fn",
"value": "ctfe-stress-const-fn"
},
{
"selected": false,
"text": "ctfe-stress-force-alloc",
"value": "ctfe-stress-force-alloc"
},
{
"selected": false,
"text": "ctfe-stress-index-check",
"value": "ctfe-stress-index-check"
},
{
"selected": false,
"text": "ctfe-stress-ops",
"value": "ctfe-stress-ops"
},
{
"selected": false,
"text": "ctfe-stress-reloc",
"value": "ctfe-stress-reloc"
},
{
"selected": false,
"text": "ctfe-stress-unsize-slice",
"value": "ctfe-stress-unsize-slice"
},
{
"selected": false,
"text": "deep-vector",
"value": "deep-vector"
},
{
"selected": false,
"text": "deeply-nested",
"value": "deeply-nested"
},
{
"selected": false,
"text": "encoding",
"value": "encoding"
},
{
"selected": false,
"text": "futures",
"value": "futures"
},
{
"selected": false,
"text": "helloworld",
"value": "helloworld"
},
{
"selected": false,
"text": "html5ever",
"value": "html5ever"
},
{
"selected": false,
"text": "hyper",
"value": "hyper"
},
{
"selected": false,
"text": "inflate",
"value": "inflate"
},
{
"selected": false,
"text": "issue-46449",
"value": "issue-46449"
},
{
"selected": false,
"text": "keccak",
"value": "keccak"
},
{
"selected": false,
"text": "piston-image",
"value": "piston-image"
},
{
"selected": false,
"text": "regex",
"value": "regex"
},
{
"selected": false,
"text": "regression-31157",
"value": "regression-31157"
},
{
"selected": false,
"text": "ripgrep",
"value": "ripgrep"
},
{
"selected": false,
"text": "script-servo",
"value": "script-servo"
},
{
"selected": false,
"text": "sentry-cli",
"value": "sentry-cli"
},
{
"selected": false,
"text": "serde",
"value": "serde"
},
{
"selected": false,
"text": "style-servo",
"value": "style-servo"
},
{
"selected": false,
"text": "syn",
"value": "syn"
},
{
"selected": false,
"text": "tokio-webpush-simple",
"value": "tokio-webpush-simple"
},
{
"selected": false,
"text": "tuple-stress",
"value": "tuple-stress"
},
{
"selected": false,
"text": "ucd",
"value": "ucd"
},
{
"selected": false,
"text": "unify-linearly",
"value": "unify-linearly"
},
{
"selected": false,
"text": "unused-warnings",
"value": "unused-warnings"
},
{
"selected": false,
"text": "webrender",
"value": "webrender"
}
],
"query": "SHOW TAG VALUES WITH KEY = bench",
"refresh": 0,
"regex": "",
"sort": 0,
"tagValuesQuery": "",
"tags": [],
"tagsQuery": "",
"type": "query",
"useTags": false
}
]
},
"time": {
"from": "now-90d",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "",
"title": "Graph",
"uid": "-QhIRphmz",
"version": 5
}
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::env;
use std::fmt;
use std::fs;
use std::path::PathBuf;
use std::str::FromStr;
extern crate serde;
use serde::{Deserialize, Serialize};
#[macro_use]
extern crate serde_derive;
extern crate chrono;
extern crate serde_json;
use chrono::prelude::*;
extern crate influx_db_client;
use influx_db_client::{Client, Point, Points, Precision, Value};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Date(pub DateTime<Utc>);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DateParseError {
pub input: String,
pub format: String,
pub error: chrono::ParseError,
}
impl FromStr for Date {
type Err = DateParseError;
fn from_str(s: &str) -> Result<Date, DateParseError> {
match DateTime::parse_from_rfc3339(s) {
Ok(value) => Ok(Date(value.with_timezone(&Utc))),
Err(error) => Err(DateParseError {
input: s.to_string(),
format: format!("RFC 3339"),
error,
}),
}
}
}
impl Serialize for Date {
fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_str(&self.0.to_rfc3339())
}
}
impl<'de> Deserialize<'de> for Date {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Date, D::Error>
where
D: serde::de::Deserializer<'de>,
{
struct DateVisitor;
impl<'de> serde::de::Visitor<'de> for DateVisitor {
type Value = Date;
fn visit_str<E>(self, value: &str) -> ::std::result::Result<Date, E>
where
E: serde::de::Error,
{
Date::from_str(value).map_err(|_| {
serde::de::Error::invalid_value(serde::de::Unexpected::Str(value), &self)
})
}
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("an RFC 3339 date")
}
}
deserializer.deserialize_str(DateVisitor)
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Commit {
pub sha: String,
pub date: Date,
}
impl Commit {
pub fn is_try(&self) -> bool {
self.date.0.naive_utc().date() == NaiveDate::from_ymd(2000, 1, 1)
}
}
impl std::hash::Hash for Commit {
fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
self.sha.hash(hasher);
}
}
impl PartialEq for Commit {
fn eq(&self, other: &Self) -> bool {
self.sha == other.sha
}
}
impl Eq for Commit {}
impl PartialOrd for Commit {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(&other))
}
}
impl Ord for Commit {
fn cmp(&self, other: &Self) -> Ordering {
self.date
.cmp(&other.date)
.then_with(|| self.sha.cmp(&other.sha))
}
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
pub struct Patch {
index: usize,
pub name: String,
path: PathBuf,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Deserialize, Serialize)]
pub enum BenchmarkState {
Clean,
Nll,
IncrementalStart,
IncrementalClean,
IncrementalPatched(Patch),
}
impl BenchmarkState {
pub fn is_base_compile(&self) -> bool {
if let BenchmarkState::Clean = *self {
true
} else {
false
}
}
pub fn is_patch(&self) -> bool {
if let BenchmarkState::IncrementalPatched(_) = *self {
true
} else {
false
}
}
pub fn name(&self) -> String {
match *self {
BenchmarkState::Clean => format!("clean"),
BenchmarkState::Nll => format!("nll"),
BenchmarkState::IncrementalStart => format!("baseline incremental"),
BenchmarkState::IncrementalClean => format!("clean incremental"),
BenchmarkState::IncrementalPatched(ref patch) => {
format!("patched incremental: {}", patch.name)
}
}
}
// Otherwise we end up with "equivalent benchmarks" looking different,
// e.g. 8-println.patch vs. 0-println.patch
pub fn erase_path(mut self) -> Self {
match &mut self {
BenchmarkState::IncrementalPatched(patch) => {
patch.index = 0;
patch.path = PathBuf::new();
}
_ => {}
}
self
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Benchmark {
pub runs: Vec<Run>,
pub name: String,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct Stat {
pub name: String,
pub cnt: f64,
}
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct Run {
pub stats: Vec<Stat>,
#[serde(default)]
pub check: bool,
pub release: bool,
pub state: BenchmarkState,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct CommitData {
pub commit: Commit,
// String in Result is the output of the command that failed
pub benchmarks: BTreeMap<String, Result<Benchmark, String>>,
pub triple: String,
}
fn main() -> Result<(), std::io::Error> {
let client = Client::new("http://localhost:8086", "rust");
for arg in env::args().skip(1) {
let file = fs::read(arg)?;
let parsed: CommitData = serde_json::from_slice(&file)?;
let mut points = Vec::new();
for (bench, result) in parsed.benchmarks {
if let Ok(Benchmark { runs, .. }) = result {
for run in runs {
let mut point = Point::new("rustc_perf");
point.add_tag("bench", Value::String(bench.clone()));
point.add_tag("state", Value::String(run.state.name().to_owned()));
point.add_field("commit", Value::String(parsed.commit.sha.clone()));
if run.check {
point.add_tag("build", Value::String("check".to_owned()));
} else if run.release {
point.add_tag("build", Value::String("release".to_owned()));
} else {
point.add_tag("build", Value::String("debug".to_owned()));
}
for stat in run.stats {
point.add_field(stat.name, Value::Float(stat.cnt));
}
point.add_timestamp(parsed.commit.date.0.timestamp());
points.push(point);
}
}
}
client.write_points(Points::create_new(points), Some(Precision::Seconds), None).unwrap();
}
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment