Skip to content

Commit

Permalink
Merge RecommendatationResultView with its common class
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 720756330
  • Loading branch information
zzzaries authored and copybara-github committed Jan 31, 2025
1 parent 47f3e44 commit 420031a
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 142 deletions.
13 changes: 7 additions & 6 deletions frontend/app/common/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const KNOWN_TOOLS = [
'memory_viewer',
'op_profile',
'pod_viewer',
'tensorflow_stats',
'framework_op_stats',
'trace_viewer',
'tf_data_bottleneck_analysis',
];
Expand Down Expand Up @@ -317,18 +317,19 @@ export function scrollBottomOfSidenav() {
}

/**
* Returns a string with an anchor tag.
* Returns a string with an anchor tag in oss.
*/
export function addAnchorTag(value: string = ''): string {
return '<a>' + value + '</a>';
export function addAnchorTag(value = '', run = ''): string {
return `<a href="/?tool=${value}&run=${run}#profile" target="_blank">${
value}</a>`;
}

/**
* Returns a string with the known tool name changed to an anchor tag.
*/
export function convertKnownToolToAnchorTag(value: string = ''): string {
export function convertKnownToolToAnchorTag(value = '', run = ''): string {
KNOWN_TOOLS.forEach(tool => {
value = value.replace(new RegExp(tool, 'g'), addAnchorTag(tool));
value = value.replace(new RegExp(tool, 'g'), addAnchorTag(tool, run));
});
return value;
}
Expand Down
2 changes: 2 additions & 0 deletions frontend/app/components/overview_page/overview_page.ng.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
>
<recommendation-result-view
recommendationResultView
[isOss]="true"
[statementInfo]="recommendationStatementInfo"
[recommendationResult]="recommendationResult">
</recommendation-result-view>
</overview-page-base>
12 changes: 12 additions & 0 deletions frontend/app/components/overview_page/overview_page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ import {takeUntil} from 'rxjs/operators';

import {OverviewPageCommon} from './overview_page_common';

const RECOMMENDATION_STATEMENT_INFO = [
{id: 'outside_compilation_statement_html'},
{id: 'eager_statement_html'},
{id: 'tf_function_statement_html'},
{id: 'statement'},
{id: 'device_collectives_statement'},
{id: 'kernel_launch_statement'},
{id: 'all_other_statement'},
{id: 'precision_statement'},
];

/** An overview page component. */
@Component({
standalone: false,
Expand All @@ -19,6 +30,7 @@ export class OverviewPage extends OverviewPageCommon implements OnDestroy {
run = '';
tag = '';
host = '';
recommendationStatementInfo = RECOMMENDATION_STATEMENT_INFO;
/** Handles on-destroy Subject, used to unsubscribe. */
private readonly destroyed = new ReplaySubject<void>(1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ xprof_ng_module(
name = "recommendation_result_view",
srcs = [
"recommendation_result_view.ts",
"recommendation_result_view_common.ts",
"recommendation_result_view_interfaces.ts",
"recommendation_result_view_module.ts",
],
assets = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,7 @@
<p>
<b>{{tipInfo.title}}</b>
</p>
<ul *ngIf="tipInfo.useClickCallback">
<li *ngFor="let tip of tipInfo.tips" (click)="onTipsClick($event)" [innerHtml]="tip"></li>
</ul>
<ul *ngIf="!tipInfo.useClickCallback">
<li *ngFor="let tip of tipInfo.tips" [innerHtml]="tip"> </li>
</ul>
<li *ngFor="let tip of tipInfo.tips" [innerHtml]="tip"></li>
</div>
</mat-card-content>
</mat-card>
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
import {Component} from '@angular/core';
import {Store} from '@ngrx/store';
import {DEFAULT_SIMPLE_DATA_TABLE} from 'org_xprof/frontend/app/common/interfaces/data_table';
import {addAnchorTag, convertKnownToolToAnchorTag} from 'org_xprof/frontend/app/common/utils/utils';
import {setCurrentToolStateAction} from 'org_xprof/frontend/app/store/actions';
import {Component, Input, OnChanges, SimpleChanges} from '@angular/core';
import {type RecommendationResult} from 'org_xprof/frontend/app/common/interfaces/data_table';
import {convertKnownToolToAnchorTag} from 'org_xprof/frontend/app/common/utils/utils';

import {RecommendationResultViewCommon} from './recommendation_result_view_common';
import {StatementData, StatementInfo, TipInfo} from './recommendation_result_view_interfaces';

const STATEMENT_INFO = [
{id: 'outside_compilation_statement_html'},
{id: 'eager_statement_html'},
{id: 'tf_function_statement_html'},
{id: 'statement'},
{id: 'device_collectives_statement'},
{id: 'kernel_launch_statement'},
{id: 'all_other_statement'},
{id: 'precision_statement'},
];

/** A recommendation result view component. */
@Component({
Expand All @@ -24,91 +12,103 @@ const STATEMENT_INFO = [
templateUrl: './recommendation_result_view.ng.html',
styleUrls: ['./recommendation_result_view.scss']
})
export class RecommendationResultView extends RecommendationResultViewCommon {
constructor(private readonly store: Store<{}>) {
super();
export class RecommendationResultView implements OnChanges {
/** The recommendation result data. */
@Input() recommendationResult?: RecommendationResult;
@Input() statementInfo: StatementInfo[] = [];
/** The variable indicating whether this is an inference. */
@Input() isInference = false;
/** The variable indicating whether this is an 3P build. */
@Input() isOss = false;

title = 'Recommendation for Next Step';
statements: StatementData[] = [];
tipInfoArray: TipInfo[] = [];

ngOnChanges(changes: SimpleChanges) {
this.parseStatements();
this.parseTips();
}

getRecommendationResultProp(id: string, defaultValue: string = ''): string {
const props = (this.recommendationResult || {}).p || {};
getRecommendationResultProp(id: string, defaultValue = ''): string {
const props =
((this.recommendationResult || {}).p || {}) as Record<string, string>;
return props[id] || defaultValue;
}

override parseStatements() {
parseStatements() {
this.statements = [];
STATEMENT_INFO.forEach(info => {
if (this.isInference) {
return;
}

this.statementInfo.forEach((info) => {
const prop = this.getRecommendationResultProp(info.id);
if (prop) {
this.statements.push({value: prop});
const statement: StatementData = {
value: prop,
};
if (info.color) {
statement.color = info.color;
}
this.statements.push(statement);
}
});
}

override parseTips() {
const data = this.recommendationResult || DEFAULT_SIMPLE_DATA_TABLE;
const hostTips: string[] = [];
const deviceTips: string[] = [];
const documentationTips: string[] = [];
const faqTips: string[] = [];
data.rows = data.rows || [];
data.rows.forEach(row => {
if (row.c && row.c[0] && row.c[0].v && row.c[1] && row.c[1].v) {
switch (row.c[0].v) {
case 'host':
hostTips.push(convertKnownToolToAnchorTag(String(row.c[1].v)));
break;
case 'device':
deviceTips.push(convertKnownToolToAnchorTag(String(row.c[1].v)));
break;
case 'doc':
documentationTips.push(String(row.c[1].v));
break;
case 'faq':
faqTips.push(String(row.c[1].v));
break;
default:
break;
parseTips() {
const tipInfoMap: {[tipType: string]: {title: string; tips: string[]}} = {};
const rows = this.recommendationResult?.rows || [];
if (rows.length === 0) {
return;
}
rows.forEach((row: google.visualization.DataObjectRow) => {
const tipType = String(row.c?.[0]?.v);
const tipString = String(row.c?.[1]?.v);
if (tipType && tipString) {
const title = String(row.c?.[2]?.v);
const queryParams = new URLSearchParams(window.parent.location.search);
const run = queryParams.get('run') || '';
const tip = this.isOss ? convertKnownToolToAnchorTag(tipString, run) :
tipString;
if (tipInfoMap[tipType]) {
tipInfoMap[tipType].tips.push(tip);
} else {
tipInfoMap[tipType] = {title, tips: [tip]};
}
}
});
const bottleneck = this.getRecommendationResultProp('bottleneck');
if (bottleneck === 'device') {
hostTips.length = 0;
} else if (bottleneck === 'host') {
deviceTips.length = 0;
}

const tipInfoArray = [
{
title: 'Tool troubleshooting / FAQ',
tips: faqTips,
},
{
title: 'Next tools to use for reducing the input time',
tips: hostTips,
useClickCallback: true,
},
{
title: 'Next tools to use for reducing the Device time',
tips: deviceTips,
useClickCallback: true,
},
{
title: 'Other useful resources',
tips: documentationTips,
},
];
this.tipInfoArray = tipInfoArray.filter(tipInfo => tipInfo.tips.length > 0);
}
const inferenceTipStyle: {[key: string]: string} = {
'color': 'navy',
'font-weight': 'bolder',
};
this.tipInfoArray = Object.entries(tipInfoMap).map(([tipType, tipInfo]) => {
const info: TipInfo = {
tipType,
title: tipInfo.title,
tips: tipInfo.tips,
};
if (tipType === 'inference') {
info.style = inferenceTipStyle;
}
return info;
});

override onTipsClick(event: Event) {
if (!event || !event.target ||
(event.target as HTMLElement).tagName !== 'A') {
return;
// Filter out tips given bottleneck.
const nonBottleneckTipTypes =
this.getRecommendationResultProp('non_bottleneck_tip_types').split(',');
if (nonBottleneckTipTypes.length > 0) {
this.tipInfoArray = this.tipInfoArray.filter(
(tipInfo) => !nonBottleneckTipTypes.includes(tipInfo.tipType));
}
const tool = (event.target as HTMLElement).innerText;
if (convertKnownToolToAnchorTag(tool) === addAnchorTag(tool)) {
this.store.dispatch(setCurrentToolStateAction({currentTool: tool}));
// Move inference tips to the top for inference run, otherwise remove them.
const inferenceTips =
this.tipInfoArray.filter((tipInfo) => tipInfo.tipType === 'inference');
this.tipInfoArray =
this.tipInfoArray.filter((tipInfo) => tipInfo.tipType !== 'inference');
if (this.isInference) {
this.tipInfoArray = [...inferenceTips, ...this.tipInfoArray];
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* Interface for statement info.
* Determines the id of the statement to be extracted from the
* recommendation result.
*/
export declare interface StatementInfo {
id: string;
color?: string;
}

/** Interface for statement data. */
export declare interface StatementData {
value: string;
color?: string;
}

/** Interface for tip info. */
export declare interface TipInfo {
title: string;
style?: {[key: string]: string};
tips: string[];
tipType: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,23 +180,32 @@ def get_recommendation_table_args(overview_page_recommendation):
table_description = [
("tip_type", "string", "tip_type"),
("link", "string", "link"),
("description", "string", "description"),
]

data = []
for faq_tip in overview_page_recommendation.faq_tips:
data.append(["faq", faq_tip.link])
data.append(["faq", faq_tip.link, "Tool troubleshooting / FAQ"])

for host_tip in overview_page_recommendation.host_tips:
data.append(["host", host_tip.link])
data.append([
"host",
host_tip.link,
"Next steps for reducing the Host time",
])

for device_tip in overview_page_recommendation.device_tips:
data.append(["device", device_tip.link])
data.append(
["device", device_tip.link, "Next steps for reducing the Device time"]
)

for doc_tip in overview_page_recommendation.documentation_tips:
data.append(["doc", doc_tip.link])
data.append(["doc", doc_tip.link, "Other useful resources"])

for inference_tip in overview_page_recommendation.inference_tips:
data.append(["inference", inference_tip.link])
data.append(
["inference", inference_tip.link, "Recommendations for inference run"]
)

bottleneck = overview_page_recommendation.bottleneck
statement = overview_page_recommendation.statement
Expand All @@ -223,6 +232,11 @@ def get_recommendation_table_args(overview_page_recommendation):
"precision_statement": precision_statement,
}

# Prop used for data filtering in the frontend.
if bottleneck in ["host", "device"]:
non_bottleneck_tip_type = "device" if bottleneck == "host" else "host"
custom_properties["non_bottleneck_tip_types"] = non_bottleneck_tip_type

return (table_description, data, custom_properties)


Expand Down
Loading

0 comments on commit 420031a

Please sign in to comment.