diff --git a/frontend/app/common/utils/utils.ts b/frontend/app/common/utils/utils.ts
index 60b4e800e..5a2627c8b 100644
--- a/frontend/app/common/utils/utils.ts
+++ b/frontend/app/common/utils/utils.ts
@@ -62,7 +62,7 @@ const KNOWN_TOOLS = [
'memory_viewer',
'op_profile',
'pod_viewer',
- 'tensorflow_stats',
+ 'framework_op_stats',
'trace_viewer',
'tf_data_bottleneck_analysis',
];
@@ -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 '' + value + '';
+export function addAnchorTag(value = '', run = ''): string {
+ return `${
+ value}`;
}
/**
* 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;
}
diff --git a/frontend/app/components/overview_page/overview_page.ng.html b/frontend/app/components/overview_page/overview_page.ng.html
index 4ee677840..7f193890d 100644
--- a/frontend/app/components/overview_page/overview_page.ng.html
+++ b/frontend/app/components/overview_page/overview_page.ng.html
@@ -8,6 +8,8 @@
>
diff --git a/frontend/app/components/overview_page/overview_page.ts b/frontend/app/components/overview_page/overview_page.ts
index 6c2ac70a1..9e648cdc3 100644
--- a/frontend/app/components/overview_page/overview_page.ts
+++ b/frontend/app/components/overview_page/overview_page.ts
@@ -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,
@@ -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(1);
diff --git a/frontend/app/components/overview_page/recommendation_result_view/BUILD b/frontend/app/components/overview_page/recommendation_result_view/BUILD
index 505d1e9c0..e27db4050 100644
--- a/frontend/app/components/overview_page/recommendation_result_view/BUILD
+++ b/frontend/app/components/overview_page/recommendation_result_view/BUILD
@@ -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 = [
diff --git a/frontend/app/components/overview_page/recommendation_result_view/recommendation_result_view.ng.html b/frontend/app/components/overview_page/recommendation_result_view/recommendation_result_view.ng.html
index 0516fe035..62d8a8d7f 100644
--- a/frontend/app/components/overview_page/recommendation_result_view/recommendation_result_view.ng.html
+++ b/frontend/app/components/overview_page/recommendation_result_view/recommendation_result_view.ng.html
@@ -12,12 +12,7 @@
{{tipInfo.title}}
-
-
+
diff --git a/frontend/app/components/overview_page/recommendation_result_view/recommendation_result_view.ts b/frontend/app/components/overview_page/recommendation_result_view/recommendation_result_view.ts
index d47eaac2b..79806cc4f 100644
--- a/frontend/app/components/overview_page/recommendation_result_view/recommendation_result_view.ts
+++ b/frontend/app/components/overview_page/recommendation_result_view/recommendation_result_view.ts
@@ -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({
@@ -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;
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];
}
}
}
diff --git a/frontend/app/components/overview_page/recommendation_result_view/recommendation_result_view_common.ts b/frontend/app/components/overview_page/recommendation_result_view/recommendation_result_view_common.ts
deleted file mode 100644
index fe4807b3a..000000000
--- a/frontend/app/components/overview_page/recommendation_result_view/recommendation_result_view_common.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import {Directive, Input, OnChanges, SimpleChanges} from '@angular/core';
-
-import {type RecommendationResult} from 'org_xprof/frontend/app/common/interfaces/data_table';
-
-interface StatementData {
- value: string;
- color?: string;
-}
-interface TipInfo {
- title: string;
- style?: {[key: string]: string};
- tips: string[];
- useClickCallback?: boolean;
-}
-
-/** A common class of recommendation result view component. */
-@Directive()
-export class RecommendationResultViewCommon implements OnChanges {
- /** The recommendation result data. */
- @Input() recommendationResult?: RecommendationResult;
-
- title = 'Recommendation for Next Step';
- statements: StatementData[] = [];
- tipInfoArray: TipInfo[] = [];
-
- ngOnChanges(changes: SimpleChanges) {
- this.parseStatements();
- this.parseTips();
- }
-
- parseStatements() {}
-
- parseTips() {}
-
- onTipsClick(event: Event) {}
-}
diff --git a/frontend/app/components/overview_page/recommendation_result_view/recommendation_result_view_interfaces.ts b/frontend/app/components/overview_page/recommendation_result_view/recommendation_result_view_interfaces.ts
new file mode 100644
index 000000000..8410bd1f0
--- /dev/null
+++ b/frontend/app/components/overview_page/recommendation_result_view/recommendation_result_view_interfaces.ts
@@ -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;
+}
diff --git a/plugin/tensorboard_plugin_profile/convert/overview_page_proto_to_gviz.py b/plugin/tensorboard_plugin_profile/convert/overview_page_proto_to_gviz.py
index 7e2a5d770..e4c256bc6 100644
--- a/plugin/tensorboard_plugin_profile/convert/overview_page_proto_to_gviz.py
+++ b/plugin/tensorboard_plugin_profile/convert/overview_page_proto_to_gviz.py
@@ -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
@@ -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)
diff --git a/plugin/tensorboard_plugin_profile/convert/overview_page_proto_to_gviz_test.py b/plugin/tensorboard_plugin_profile/convert/overview_page_proto_to_gviz_test.py
index 7eabc01d7..c5e6b0283 100644
--- a/plugin/tensorboard_plugin_profile/convert/overview_page_proto_to_gviz_test.py
+++ b/plugin/tensorboard_plugin_profile/convert/overview_page_proto_to_gviz_test.py
@@ -81,12 +81,21 @@ def setUpClass(cls):
"MockTip", [
"tip_type",
"link",
+ "description",
])
-
+ tip_description_map = {
+ "faq": "Tool troubleshooting / FAQ",
+ "host": "Next steps for reducing the Host time",
+ "device": "Next steps for reducing the Device time",
+ "doc": "Other useful resources",
+ "inference": "Recommendations for inference run",
+ }
ProtoToGvizTest.mock_tips = []
for tip in ["faq", "host", "device", "doc"]:
for idx in range(0, 3):
- ProtoToGvizTest.mock_tips.append(MockTip(tip, tip + "_link" + str(idx)))
+ ProtoToGvizTest.mock_tips.append(
+ MockTip(tip, tip + "_link" + str(idx), tip_description_map[tip])
+ )
# Checks that DataTable columns match schema defined in table_description.
def check_header_row(self, data, table_description, row_values):
@@ -290,7 +299,7 @@ def test_recommendation_empty(self):
"Empty table should have 0 rows.")
# Check the number of Overview Page Recommendation data table columns.
# One for tip_type, and one for link
- self.assertLen(data_table.columns, 2)
+ self.assertLen(data_table.columns, 3)
def test_recommendation_simple(self):
recommendation = self.create_mock_recommendation()
@@ -305,8 +314,8 @@ def test_recommendation_simple(self):
list(self.mock_tips), data_table.NumberOfRows(),
"Simple table has 12 rows.")
# Check the number of columns in table descriptor and data table.
- self.assertLen(table_description, 2)
- self.assertLen(data_table.columns, 2)
+ self.assertLen(table_description, 3)
+ self.assertLen(data_table.columns, 3)
# Check data against mock values.
for idx, row in enumerate(data):