Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Tabular View for comparing baseline and test builds for selected platforms and metrics #131

Merged
merged 1 commit into from
Aug 29, 2019

Conversation

awsafsakif
Copy link
Contributor

@awsafsakif awsafsakif commented Aug 6, 2019

  • Closes AdoptOpenJDK#37
  • Current filters: benchmark, platform, cell color
  • Allows comparison between different jdk versions and types
  • Cell on click redirects to Perf Compare
  • Perf Compare changed to fill in values from URL on load
  • Added sdkResource to parser, will be added as field to database

Co-authored-by: Piyush Gupta piyush286@gmail.com

Signed-off-by: Awsaf Arefin Sakif awsaf.sakif@ibm.com

@awsafsakif
Copy link
Contributor Author

awsafsakif commented Aug 6, 2019

TabularViewExample

Including a screenshot of how the view looks like at the moment.

@awsafsakif
Copy link
Contributor Author

@piyush286 please take a look and let me know what needs to be changed.

@piyush286
Copy link
Contributor

@pinicman Yeah finally! Thanks for making all the changes suggested during our meetings. I'll try to review it later this week.

@karianna karianna added the enhancement New feature or request label Aug 7, 2019
@karianna karianna added this to In progress in aqa-test-tools via automation Aug 7, 2019
@awsafsakif awsafsakif force-pushed the tabular branch 2 times, most recently from b093248 to 12ae94c Compare August 12, 2019 20:34
}
}
// Return the list of unique platforms for column generation
const uniquePlatforms = [...new Set(datas.map(item => item.buildName))];
Copy link
Contributor

@piyush286 piyush286 Aug 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be a bit confusing since we are returning the buildName for platforms. The front end extracts the proper names for platforms. Even though this might reduce processing on the backend, we should still document and change uniquePlatforms to something like buildNames.

@@ -24,7 +24,8 @@
"react-jsx-highcharts": "^3.5.0",
"react-jsx-highstock": "^3.5.0",
"react-router": "^5.0.0",
"react-router-dom": "^5.0.0"
"react-router-dom": "^5.0.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to add package-lock.json file since we're updating this file.

const month = (new Date().getMonth() + 1).toString(); //Current Month
const year = (new Date().getFullYear()).toString(); //Current Year

const jdkDate = year + ((month.length < 2) ? "0" + month : month) + ((date.length < 2) ? "0" + date : date)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to some comment here saying that we're making an assumption that the JDK data would be in the format YYYYMMDD. Same thing in dateTransform().

const jdkDate = year + ((month.length < 2) ? "0" + month : month) + ((date.length < 2) ? "0" + date : date)

!('baselineJdkDate' in this.state) && (this.state.baselineJdkDate = jdkDate);
!('baselineJdkVersion' in this.state) && (this.state.baselineJdkVersion = 'O8');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again! These are our assumptions so we should list them all out along with an example.

let column = {
Header: 'Benchmark Name',
accessor: 'benchmarkName',
Cell: props => <span>{props.value.split(",")[0]} <br/> {props.value.split(",")[1]} <br/> {props.value.split(",")[2]}</span>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a comment saying that we're using those 3 breaks to show benchmark name, variant and metric in different lines.

color = '#F0F755';}
else if (val === 'N/A') {
color = 'grey';}
else if (val > 200) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different color is fine but we shouldn't show the actual value for it since it's going to be confusing since we'll be showing the relative comparison percent for most of the cells.

The relative comparison value could be more than 200% in some rare cases such as converters and DAA tests so those cells should still be considered green.

this.setState({columns:newArray, originalColumns: newArray});
}
// Set cell color based on comparison value
handleRegression (val) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to handle 2 cases, which right now we are just handling as one:

  1. One of the builds is missing data
  2. Relative comparison value is way more than 100 (i.e. 200x or 1000x).

Baseline JDK: {this.handleProp(props.value, 'baselineJdk')} <br/>
Baseline Sdk Resource: {this.handleProp(props.value, 'baselineSdkResource')}
</div> }>
<span onClick={() => this.handleLink(this.handleProp(props.value, 'buildUrl'))}> {this.handleProp(props.value, 'compare')} </span></Tooltip>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add % to the relative comparison value to make it more clear.

const newArray = [];
let column = {
Header: 'Benchmark Name',
accessor: 'benchmarkName',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to give it a better name. Since benchmarkName just stores the benchmark name in the database, we should replace all instance of this var with something like benchmarkNVM (i.e. benchmark name + variant + metric).

platform = element.buildName.split("_").slice(4).join('_');
for (const metric in element.aggregateInfo[0].metrics) {
found = false;
benchmarkName = element.aggregateInfo[0].benchmarkName + ',' + element.aggregateInfo[0].benchmarkVariant + "," + element.aggregateInfo[0].metrics[metric].name;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use benchmarkNVM instead of benchmarkName as mentioned above.

benchmarkName = element.aggregateInfo[0].benchmarkName + ',' + element.aggregateInfo[0].benchmarkVariant + "," + element.aggregateInfo[0].metrics[metric].name;

for (const currentEntry in newArray) {
// If benchmark aleady exists append to it
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small typo!

let benchmarkName = "";
let found = false;

data.forEach(function (element) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should use a more meaningful name such as testResultsObject instead of element. We should do the same thing for all other loops as well.

}

populateTable(data, type) {
/* Entry format, each entry in the array is an object with two fields benchmarkName and platforms
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

platforms name is confusing since it has more info beside just platform info. We should call it something like platformsSpecificData.

/* Entry format, each entry in the array is an object with two fields benchmarkName and platforms
platforms is an object with each field being a separate platform containing the jdk data such as score, date, CI */
const newArray = [];
let entry = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use newObj instead of entry. We should change all other functions as well for this.

let variant = testEntry.benchmarkName.split(",")[1];
let metric = testEntry.benchmarkName.split(",")[2];

let topLevel = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use benchmarkLevel instead of topLevel. Same thing for midLevel and child.


let topLevel = {};
let midLevel = {};
let child = {title: metric, value: testEntry.benchmarkName};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should mention that we are giving unique titles for these values in order to avoid showing metrics for all benchmark variants. By setting titles, we limit the display to one variant, requiring user to manually select different variants in case one wants to look up the metric for multiple variants. For example, footprint metric exists in multiple Liberty variants such as DT7, DT3 and AcmeAir.

import benchmarkVariantsInfo from '../PerfCompare/lib/benchmarkVariantsInfo';

const { Panel } = Collapse;
const { SHOW_PARENT } = TreeSelect;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add the purpose for this.

}
}

colorFilter(caller) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should explain that we have 2 filters: benchmark filter and color filter. We always call the benchmark filter first, which uses the originalData to limit to the selected benchmarks. Then we call the colorFilter, which further limits the data based on relative comparison values.

Also, we should use a better name such as firstFilter than caller since it doesn't really tell its purpose. If firstFilter is true, then we need to call the benchmark filter first. If it's false, then it means that we've already done filtering based on benchmarks, and hence, we should apply the color filter now.

let filterValue;
// If color filter is set to ALL, do not not apply filter
if (this.state.colorFilter === "all") {return;}
else if (this.state.colorFilter === "yellow") {filterValue = 98;}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use range instead of absolute numbers.

for (let i=0; i < this.state.consolidatedData.length; i++) {
for (let platform in this.state.consolidatedData[i].platforms) {
// Set values to N/A if they exceed the filterValue
if (parseInt(this.state.consolidatedData[i].platforms[platform].compare) > filterValue) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you choose yellow, this would also show the red cells.

<span> Please choose the color filter: </span><select name="colorFilter" value={this.state.colorFilter} onChange={this.handleColorFilter.bind(this)}>
<option value="all">All</option>
<option value="red">Red</option>
<option value="yellow">Yellow</option>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not that we care much about green, but we can just add it since it's simple.

if (element.platforms.hasOwnProperty(platform)) {
entry.platforms[platform] = {...testEntry.platforms[platform], ...element.platforms[platform]};
if (higherBetter) {
entry.platforms[platform].compare = Number(testEntry.platforms[platform].testScore * 100 / element.platforms[platform].baselineScore).toFixed(2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have time, maybe we have some extra check to take care of high confidence interval.

If CI 1 + CI 2 < Regression +0.7%, then it's a confirmed regression. Else, we should show some symbol (i.e. warning sign or something) to show that confidence interval is high, and hence, we can't confirm the regression.

aqa-test-tools automation moved this from In progress to Needs review Aug 13, 2019
Copy link
Contributor

@piyush286 piyush286 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pinicman Thanks Awsaf for putting this together. As we discussed in our meeting today, let's update the code with the small changes that we decided.

@awsafsakif awsafsakif force-pushed the tabular branch 2 times, most recently from 68ea17e to e56ebd9 Compare August 19, 2019 14:28
let color;
if (val === 0) {
color = '#ffdbac';}
else if (val <= 90) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use both upper and lower ranges just to make it more robust. Otherwise, we're relying on the order of if else statements.

<div className="row">
<div className="column" style={colStyle}> SDK Resource <br/> </div>
<div className="column"> <select name="testSdkResource" className="select-css" value={this.state.testSdkResource} onChange={this.handleChange.bind(this)}>
<option value="releases">Releases</option>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add null as well in case this isn't set.

Copy link
Contributor

@piyush286 piyush286 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pinicman Thanks for making all the minor changes. Just 1-2 more things and then we're good to deliver it.

@awsafsakif awsafsakif force-pushed the tabular branch 3 times, most recently from f769e86 to e728068 Compare August 20, 2019 16:34
Copy link
Contributor

@piyush286 piyush286 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@llxia Thanks for all the suggestions so far. Awsaf and I have reviewed it a few times, and things seem to be more polished now. Can you please take a look and merge it if this MVP looks good? Thanks!

module.exports = async ( req, res ) => {
const data = [];
const db = new TestResultsDB();
//console.log("Request received: ", req.query.jdkVersion, req.query.jvmType, req.query.jdkDate, req.query.sdkResource);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the comment.


const datas = [];
// TODO: Use available api to get build directly
const query = {buildName: {$regex: ".*_" + 'openjdk' + req.query.jdkVersion.substring(1) + "_" + req.query.jvmType + ".*perf_.*"}};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the value is 08, 011, 012 and we try to trim it here jdkVersion.substring(1)? Can we just use the correct value?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's not needed. We'll fix it since jdkVersion already has the java stream info it.


if (latestRun !== undefined) {datas.push( latestRun );}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is very costly. There is no need to use distinct twice. And we do not need to query one record at a time for all benchmarks x platforms and write logic to filter out data by date.

We should be able to do most of it in one query and do some light processing. We can query by the date and buildName and sort jdkBuildDateUnixTime in testData group on the buildName and benchmarkName.

Please create an issue to fix this. If you have time, please work on the issue.

// Return the list of unique pipeline names for column generation
const buildNames = [...new Set(datas.map(item => item.buildName))];
datas.push(buildNames);
res.send( await Promise.all(datas) );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not need Promise.all here.


this.state.inputURL[key] = value;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • instead of directly setting the value in the state, please use setState()

  • We should use the existing function getParams() and it will return an object of key and value. For example,

getParams(window.location.search)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice suggestion! We'll do the same in TabularView.jsx as well. Thanks!

<div className="column"> <select name="testJdkVersion" className="select-css" value={this.state.testJdkVersion} onChange={this.handleChange.bind(this)}>
<option value="O8">OpenJDK8</option>
<option value="O11">OpenJDK11</option>
<option value="O12">OpenJDK12</option>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not use the dropdown and hardcode jdk versions. It is a lot of work to update this for every release. An input box with a default value is a better choice. Or if you want to use dropdown, then the data should come from the database.

Same for the rest of dropdown. And each of them should be a react component.
If you do not have time to fix it now, please open an issue for it.

Copy link
Contributor

@piyush286 piyush286 Aug 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this one definitely makes sense. Else, we'll have to update these options whenever there's a new java version or type. Will soft-code these options and get it from the database.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@llxia Soft-coding these options would add more distinct in the code. Are you fine with the more additions or do we leave this hard-coded for now? Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nvm! We found a more efficient way to do it by grouping with $addToSet, so we are soft-coding all those options.

@llxia
Copy link
Contributor

llxia commented Aug 21, 2019

Please format the code. Thanks.

@piyush286
Copy link
Contributor

piyush286 commented Aug 21, 2019

This code is very costly. There is no need to use distinct twice. And we do not need to query one record at a time for all benchmarks x platforms and write logic to filter out data by date.

We should be able to do most of it in one query and do some light processing. We can query by the date and buildName and sort jdkBuildDateUnixTime in testData group on the buildName and benchmarkName.

There is no need to use distinct twice.

@llxia Could you please clarify how can we get the distinct platforms, benchmark names and others such as Java version, type, SDK Resource (once we soft-code by querying from the database) without using distinct? Will check whether there's any other efficient way of doing it but haven't seen one yet.

We can query by the date and buildName and sort jdkBuildDateUnixTime in testData group on the buildName and benchmarkName.

I don't think we can query by a specific date. We want to get the latest JDK build that was built on or "before" the selected JDK date so we can't really query for one particular date. We use the jdkBuildDateUnixTime only when the latest JDK build that met the JDK date filter has multiple runs. So it could be the case that some JDK with a older built data could have a newer jdkBuildDateUnixTime. Currently, we're looking at the JDK built data in benchmarkProduct, something that we agreed to use for now but could be changed later.

Since we care about the latest run for each benchmark and platform, I'm not sure how we can query in any other way. Sorry, I'm not getting your point. If that's the case, then could you please put the pseudo step-by-step code for it? We could also have a quick meeting if that's more convenient for you.

@piyush286
Copy link
Contributor

We'll be adding another dropdown menu for build server so that we can compare builds only from one specific server since it wouldn't make sense to compare the data that would have run on different machines under different servers. Since we already store the server info under url, we can use that to limit the data if needed.

@llxia
Copy link
Contributor

llxia commented Aug 23, 2019

This code is very costly. There is no need to use distinct twice. And we do not need to query one record at a time for all benchmarks x platforms and write logic to filter out data by date.
We should be able to do most of it in one query and do some light processing. We can query by the date and buildName and sort jdkBuildDateUnixTime in testData group on the buildName and benchmarkName.

There is no need to use distinct twice.

@llxia Could you please clarify how can we get the distinct platforms, benchmark names and others such as Java version, type, SDK Resource (once we soft-code by querying from the database) without using distinct? Will check whether there's any other efficient way of doing it but haven't seen one yet.

We can query by the date and buildName and sort jdkBuildDateUnixTime in testData group on the buildName and benchmarkName.

I don't think we can query by a specific date. We want to get the latest JDK build that was built on or "before" the selected JDK date so we can't really query for one particular date. We use the jdkBuildDateUnixTime only when the latest JDK build that met the JDK date filter has multiple runs. So it could be the case that some JDK with a older built data could have a newer jdkBuildDateUnixTime. Currently, we're looking at the JDK built data in benchmarkProduct, something that we agreed to use for now but could be changed later.

Since we care about the latest run for each benchmark and platform, I'm not sure how we can query in any other way. Sorry, I'm not getting your point. If that's the case, then could you please put the pseudo step-by-step code for it? We could also have a quick meeting if that's more convenient for you.

I just think there should be a more efficient way of doing the query. I would like to have an issue open so we can revisit this.

@piyush286
Copy link
Contributor

piyush286 commented Aug 23, 2019

@llxia Sounds good, Lan! Opened an issue for it: #133.

Adding the dropdown option for server (i.e. url) as mentioned in #131 (comment) would help a bit in reducing the query data. We are working on it and will update this PR once that's done.

…ed platforms and metrics

- Closes AdoptOpenJDK#37
- Current filters: benchmark, platform, cell color
- Allows comparison between different jdk versions and types
- Cell on click redirects to Perf Compare
- Perf Compare changed to fill in values from URL on load
- Added sdkResource to parser, will be added as field to database
- Warning sign appears if total CI exceeds percentage difference

Co-Authored-By: Piyush Gupta <piyush286@gmail.com>
Signed-off-by: Awsaf Arefin Sakif <awsaf.sakif@ibm.com>
@piyush286
Copy link
Contributor

@llxia Could you please take a final look whenever you get a chance? Thanks!

aqa-test-tools automation moved this from Needs review to Reviewer approved Aug 29, 2019
@llxia
Copy link
Contributor

llxia commented Aug 29, 2019

Please update the code on the internal server. And we should enable the pref builds and ensure these builds are monitored by TRSS. Thanks.

@llxia llxia merged commit 2c5e1b8 into adoptium:master Aug 29, 2019
aqa-test-tools automation moved this from Reviewer approved to Done Aug 29, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

Tabular View for Comparing Baseline and Test Builds for Selected Platforms & Metrics
4 participants