From de3a642f2e19aa3d2fbbb947a436872bb52fe31b Mon Sep 17 00:00:00 2001 From: heiheihei <1395202740@qq.com> Date: Thu, 28 Aug 2025 20:22:29 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E7=9A=84=E5=B1=95=E7=A4=BA=E9=9A=90=E8=97=8F?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/src/ModelStructure/Fsg/Filter.tsx | 12 ++------ .../app/src/ModelStructure/Fsg/index.tsx | 29 ++++++++++--------- .../ModelVis/app/src/libs/file-dialog.ts | 2 +- 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/Filter.tsx b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/Filter.tsx index badca84ee..a430e97e5 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/Filter.tsx +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/Filter.tsx @@ -17,7 +17,7 @@ import { useI18n } from 'hooks' import { joinCls, openDialog } from 'libs' import { useEffect, useState } from 'react' import { useAtomValue } from "jotai" -import { fsgVisibleAtom, modelPathAtom } from "../../stores" +import { modelPathAtom } from "../../stores" import { toast } from "sonner"; import { Button } from 'antd' @@ -29,7 +29,7 @@ const commonInputNumberClass: string = joinCls( 'dark:focus:bg-dark-primary dark:focus:text-white' ) const Filter = (props: { - onQuery: (repeat: number, min: number, max: number) => Promise; onToggle: () => void; + onQuery: (repeat: number, min: number, max: number) => Promise; exportOpDuration: (path: string) => Promise; }) => { const t = useI18n() @@ -38,7 +38,6 @@ const Filter = (props: { const [maxNodes, setMaxNodes] = useState(3) const [loadingExportBtn, setLoadingExportBtn] = useState(false) const [loadingQueryBtn, setLoadingQueryBtn] = useState(false) - const visible = useAtomValue(fsgVisibleAtom) const path = useAtomValue(modelPathAtom) const handleInput = (e: React.FormEvent, type: string): void => { @@ -68,10 +67,6 @@ const Filter = (props: { setLoadingQueryBtn(false) } - const toggleFsgVis = (): void => { - props.onToggle() - } - const exportCSV = async (): Promise => { const path = await openDialog('csv') @@ -104,9 +99,6 @@ const Filter = (props: { onInput={(e) => handleInput(e, 'max')} /> - } diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx index 152b6774c..bfbd378ba 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx @@ -30,6 +30,7 @@ import { useEffect, useRef, useState } from 'react' import { workerInitFsgs, workerToggleFsgs } from '../../worker-apis' import { Resizer, type ResizerType } from 'ui/Resizer' import Pushpin from "icons/pushpin.svg?react" +import { isEqual } from 'lodash' /** * The current algorithm is flawed, results returned are duplicated, @@ -194,21 +195,16 @@ const Fsg = () => { setLoading(false) } - const toggleFsg = (): void => { - setFsgVis(!fsgVis) - workerToggleFsgs(!fsgVis) - if (!fsgVis) { - workerInitFsgs(selectedRow?.nodes ?? []) - } - } - const onRow = (record: FsgTableRow) => { return { onClick: () => { - setSelectedRow(record) - if (fsgVis) { - workerInitFsgs(record.nodes) + if (isEqual(record, selectedRow)) { + setSelectedRow(undefined) + workerInitFsgs([]) + return } + setSelectedRow(record) + workerInitFsgs(record.nodes) } } } @@ -293,10 +289,15 @@ const Fsg = () => { /> - + { className: '!my-2', pageSizeOptions: ['10', '20', '50', '100'], total: fsgData.length, - showTotal: (total: number): string => t('fsg.paginationTotal', { total: total }), + showTotal: (total: number): string => t('fsg.paginationTotal', { total }), showSizeChanger: true, showQuickJumper: true }} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src/libs/file-dialog.ts b/plugins/mindstudio-insight-plugins/ModelVis/app/src/libs/file-dialog.ts index 52b4e34fd..9bc692bda 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src/libs/file-dialog.ts +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src/libs/file-dialog.ts @@ -18,7 +18,7 @@ import { type DialogFilter, open } from "@tauri-apps/plugin-dialog" const EntryFilter: Record = { pb: { name: "pb format model", - extensions: ["onnx", "mindir", "geir", "pbtxt"] + extensions: ["onnx", "mindir", "geir", "pbtxt", "dot"] }, csv: { name: "operator csv", -- Gitee From eb10509376139cb5100307114ef429c30e3a5141 Mon Sep 17 00:00:00 2001 From: heiheihei <1395202740@qq.com> Date: Fri, 29 Aug 2025 17:15:17 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E9=87=8D=E5=A4=8D=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E5=8F=AF=E4=BB=A5=E8=BF=9B=E8=A1=8C=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/src/ModelStructure/Fsg/index.tsx | 69 ++++++++++++++++--- .../ModelVis/app/src/icons/up.svg | 3 + .../ModelVis/app/src/types/model.d.ts | 1 + 3 files changed, 64 insertions(+), 9 deletions(-) create mode 100644 plugins/mindstudio-insight-plugins/ModelVis/app/src/icons/up.svg diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx index bfbd378ba..2076ed2b5 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx @@ -13,14 +13,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { type I18nFunc, useI18n } from 'hooks' +import { type I18nFunc, useAnimatedTranslate, useI18n } from 'hooks' import { useAtomValue } from 'jotai' import { joinCls } from 'libs' import Filter from './Filter' import { modelPathAtom, fsgPanelVisibleAtom, - fsgVisibleAtom, themeAtom, currentGraphAtom + themeAtom, + currentGraphAtom, + zoomAtom, + nodesEdgesAtom } from '../../stores' import { useAtom } from 'jotai/index' import { invoke } from '@tauri-apps/api/core' @@ -30,6 +33,7 @@ import { useEffect, useRef, useState } from 'react' import { workerInitFsgs, workerToggleFsgs } from '../../worker-apis' import { Resizer, type ResizerType } from 'ui/Resizer' import Pushpin from "icons/pushpin.svg?react" +import UpIcon from "icons/up.svg?react" import { isEqual } from 'lodash' /** @@ -70,7 +74,8 @@ const dedup = (graphs: FsgRet[]): FsgTableRow[] => { totalDuration: meanDuration * nodes.length, meanMteTime, totalMteTime: meanMteTime * nodes.length, - nodes + nodes, + fsgIndex: 0, } }).filter(item => item.count > 0 && item.nodeCount > 0) } @@ -80,14 +85,40 @@ const sortFn = (lhs: FsgRet, rhs: FsgRet) => const sortedFsgs = (raw: FsgRet[]) => raw.sort(sortFn) -const getColumns = (t: I18nFunc): ColumnsType => { +const getColumns = (t: I18nFunc, jumpFsg: (record: FsgTableRow) => void): ColumnsType => { return [ { dataIndex: 'fsg', title: t('fsg.fsg'), width: 200, ellipsis: true, - sorter: (a: FsgTableRow, b: FsgTableRow): number => a.fsg.localeCompare(b.fsg) + sorter: (a: FsgTableRow, b: FsgTableRow): number => a.fsg.localeCompare(b.fsg), + render: (value, record) => { + return
+ +
+ { + if (record.fsgIndex > 0) { + record.fsgIndex-- + jumpFsg(record) + } + }} + /> + { + if (record.fsgIndex < record.nodes.length - 1) { + record.fsgIndex++ + jumpFsg(record) + } + }} + /> +
+ {value} +
+ } }, { dataIndex: 'count', @@ -151,13 +182,25 @@ const Fsg = () => { const [top, setTop] = useState(DEFAULT_TOP) const [left, setLeft] = useState(10) const [isLock, setIsLock] = useState(false) - const [fsgVis, setFsgVis] = useAtom(fsgVisibleAtom) const path = useAtomValue(modelPathAtom) const [fsgData, setFsgData] = useState([]) const [selectedRow, setSelectedRow] = useState() const currentGraph = useAtomValue(currentGraphAtom) - const columns = getColumns(t) + const jumpFsg = (record: FsgTableRow) => { + const { fsgIndex, nodes } = record + const firstNode = data?.nodes.find(node => (nodes[fsgIndex][0] === node.id)) + if (firstNode !== undefined) { + startAnimation({ + x: zoom * (innerWidth / 2 - firstNode.x - firstNode.width / 2), + y: zoom * (innerHeight / 4 - firstNode.y - firstNode.height / 2) + }, 500) + } + } + const columns = getColumns(t, jumpFsg) + const zoom = useAtomValue(zoomAtom) + const startAnimation = useAnimatedTranslate() + const data = useAtomValue(nodesEdgesAtom) const globalTheme = useAtomValue(themeAtom) const queryFsg = async (repeat: number, min: number, max: number) => { @@ -197,18 +240,26 @@ const Fsg = () => { const onRow = (record: FsgTableRow) => { return { - onClick: () => { + onClick: (e: Event) => { + const isJumpFsgIcon = document.getElementById(`jumpFsg_${record.fsg}`)?.contains(e.target as HTMLElement) + + if (isJumpFsgIcon) { + return + } + if (isEqual(record, selectedRow)) { setSelectedRow(undefined) - workerInitFsgs([]) + workerToggleFsgs(false) return } setSelectedRow(record) + jumpFsg(record) workerInitFsgs(record.nodes) } } } + const changeWidth: ResizerType['callback'] = (value, _) => { if (containerRef.current === null) { return diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src/icons/up.svg b/plugins/mindstudio-insight-plugins/ModelVis/app/src/icons/up.svg new file mode 100644 index 000000000..e4fbc86bc --- /dev/null +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src/icons/up.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src/types/model.d.ts b/plugins/mindstudio-insight-plugins/ModelVis/app/src/types/model.d.ts index e04d036a1..8d44134b0 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src/types/model.d.ts +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src/types/model.d.ts @@ -129,6 +129,7 @@ type FsgTableRow = { meanMteTime: number totalMteTime: number nodes: string[][] + fsgIndex: number } type TimeType = 'avg_duration_us' | 'avg_memory_move_time_us' -- Gitee From 13f7fd43a5593d040406d84a79ea091f7f84a7b0 Mon Sep 17 00:00:00 2001 From: heiheihei <1395202740@qq.com> Date: Fri, 29 Aug 2025 17:51:40 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E9=87=8D=E5=A4=8D=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ModelVis/app/src/ModelStructure/Fsg/index.tsx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx index 2076ed2b5..c88fe73e8 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx @@ -85,7 +85,7 @@ const sortFn = (lhs: FsgRet, rhs: FsgRet) => const sortedFsgs = (raw: FsgRet[]) => raw.sort(sortFn) -const getColumns = (t: I18nFunc, jumpFsg: (record: FsgTableRow) => void): ColumnsType => { +const getColumns = (t: I18nFunc, jumpFsg: (record: FsgTableRow) => void, workerInitFsgs: (nodes: string[][]) => void): ColumnsType => { return [ { dataIndex: 'fsg', @@ -103,6 +103,7 @@ const getColumns = (t: I18nFunc, jumpFsg: (record: FsgTableRow) => void): Column if (record.fsgIndex > 0) { record.fsgIndex-- jumpFsg(record) + workerInitFsgs([record.nodes[record.fsgIndex]]) } }} /> @@ -112,6 +113,7 @@ const getColumns = (t: I18nFunc, jumpFsg: (record: FsgTableRow) => void): Column if (record.fsgIndex < record.nodes.length - 1) { record.fsgIndex++ jumpFsg(record) + workerInitFsgs([record.nodes[record.fsgIndex]]) } }} /> @@ -197,7 +199,7 @@ const Fsg = () => { }, 500) } } - const columns = getColumns(t, jumpFsg) + const columns = getColumns(t, jumpFsg, workerInitFsgs) const zoom = useAtomValue(zoomAtom) const startAnimation = useAnimatedTranslate() const data = useAtomValue(nodesEdgesAtom) @@ -240,7 +242,7 @@ const Fsg = () => { const onRow = (record: FsgTableRow) => { return { - onClick: (e: Event) => { + onClick: (e: React.MouseEvent) => { const isJumpFsgIcon = document.getElementById(`jumpFsg_${record.fsg}`)?.contains(e.target as HTMLElement) if (isJumpFsgIcon) { @@ -254,12 +256,11 @@ const Fsg = () => { } setSelectedRow(record) jumpFsg(record) - workerInitFsgs(record.nodes) + workerInitFsgs([record.nodes[record.fsgIndex]]) } } } - const changeWidth: ResizerType['callback'] = (value, _) => { if (containerRef.current === null) { return -- Gitee From 4957efad514bfebc258bc279b27b2ad4a440c61c Mon Sep 17 00:00:00 2001 From: heiheihei <1395202740@qq.com> Date: Sat, 30 Aug 2025 10:14:29 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E9=87=8D=E5=A4=8D=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E5=85=A8=E9=87=8F=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/src/ModelStructure/Fsg/Filter.tsx | 46 +-- .../app/src/ModelStructure/Fsg/FsgTable.tsx | 300 ++++++++++++++++++ .../app/src/ModelStructure/Fsg/index.tsx | 272 +--------------- .../ModelVis/app/src/locales/en.json | 3 +- .../ModelVis/app/src/locales/zh-CN.json | 3 +- 5 files changed, 335 insertions(+), 289 deletions(-) create mode 100644 plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/FsgTable.tsx diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/Filter.tsx b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/Filter.tsx index a430e97e5..31505db3a 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/Filter.tsx +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/Filter.tsx @@ -19,7 +19,7 @@ import { useEffect, useState } from 'react' import { useAtomValue } from "jotai" import { modelPathAtom } from "../../stores" import { toast } from "sonner"; -import { Button } from 'antd' +import { Button, Switch } from 'antd' const commonInputNumberClass: string = joinCls( 'h-8 w-12 pr-0.5 pl-1 text-sm mx-1', @@ -31,6 +31,8 @@ const commonInputNumberClass: string = joinCls( const Filter = (props: { onQuery: (repeat: number, min: number, max: number) => Promise; exportOpDuration: (path: string) => Promise; + isFullDisplay: boolean; + setIsFullDisplay: (value: boolean) => void; }) => { const t = useI18n() const [repeatTimes, setRepeatTimes] = useState(2) @@ -83,23 +85,31 @@ const Filter = (props: { setLoadingExportBtn(false) }, [path]) - return
- {t('fsg.minRepeatTimes')} - handleInput(e, 'repeat')} - /> - {t('fsg.nodeInFsg')} - handleInput(e, 'min')} - /> - ~ - handleInput(e, 'max')} - /> - - + return
+
+ {t('fsg.minRepeatTimes')} + handleInput(e, 'repeat')} + /> +
+
+ {t('fsg.nodeInFsg')} + handleInput(e, 'min')} + /> + ~ + handleInput(e, 'max')} + /> +
+ + +
+ {t('fsg.allStructures')} + props.setIsFullDisplay(value)} /> +
} diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/FsgTable.tsx b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/FsgTable.tsx new file mode 100644 index 000000000..7e1ba0ef0 --- /dev/null +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/FsgTable.tsx @@ -0,0 +1,300 @@ +// Copyright (c) 2025, Huawei Technologies Co., Ltd. +// All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { type I18nFunc, useAnimatedTranslate, useI18n } from 'hooks' +import { useAtomValue } from 'jotai' +import Filter from './Filter' +import { + modelPathAtom, + themeAtom, + currentGraphAtom, + zoomAtom, + nodesEdgesAtom +} from '../../stores' +import { invoke } from '@tauri-apps/api/core' +import type { ColumnsType } from 'antd/es/table' +import { ConfigProvider, message, Table, theme } from 'antd' +import { useEffect, useState } from 'react' +import { workerInitFsgs, workerToggleFsgs } from '../../worker-apis' +import UpIcon from "icons/up.svg?react" +import { isEqual } from 'lodash' +import { clsx } from 'libs' + +let opDuration: OpDuration = {} +let fsgDataCach: FsgRet[] = [] + +const getOpStructAvgTime = (opStruct: string, type: TimeType): number => { + const opList = opStruct.split(',') + let avgTime = 0 + opList.forEach(op => (avgTime += opDuration[op]?.[type] ?? 0)) + return avgTime +} + +const dedup = (graphs: FsgRet[]): FsgTableRow[] => { + const seen = new Set() + return graphs.map((item, index) => { + const dup_nodes = item.instances.map(i => i.node_ids.map(n => n.nid)) + + const nodes = dup_nodes.filter(arr => { + const sorted = arr.toSorted() + const key = sorted.join('') + return seen.has(key) ? false : seen.add(key) && true + }) + + const meanDuration = getOpStructAvgTime(item.optype_struct, 'avg_duration_us') + const meanMteTime = getOpStructAvgTime(item.optype_struct, 'avg_memory_move_time_us') + + return { + key: `${item.optype_struct}_${index}_${meanDuration}_${meanMteTime}`, + fsg: item.optype_struct, + count: nodes.length, + nodeCount: item.instances[0]?.node_num ?? 0, + meanDuration, + totalDuration: meanDuration * nodes.length, + meanMteTime, + totalMteTime: meanMteTime * nodes.length, + nodes, + fsgIndex: 0, + } + }).filter(item => item.count > 0 && item.nodeCount > 0) +} + +const sortFn = (lhs: FsgRet, rhs: FsgRet) => + (rhs.instances[0]?.node_num ?? 0) - (lhs.instances[0]?.node_num ?? 0) + +const sortedFsgs = (raw: FsgRet[]) => raw.sort(sortFn) + +const getColumns = (t: I18nFunc, jumpFsg: (record: FsgTableRow) => void, workerInitFsgs: (nodes: string[][]) => void): ColumnsType => { + return [ + { + dataIndex: 'fsg', + title: t('fsg.fsg'), + width: 200, + ellipsis: true, + sorter: (a: FsgTableRow, b: FsgTableRow): number => a.fsg.localeCompare(b.fsg), + render: (value, record) => { + return
+
+ 0 ? 'cursor-pointer hover:text-blue-800' : 'cursor-not-allowed', + )} + onClick={() => { + if (record.fsgIndex > 0) { + record.fsgIndex-- + jumpFsg(record) + workerInitFsgs([record.nodes[record.fsgIndex]]) + } + }} + /> + { + if (record.fsgIndex < record.nodes.length - 1) { + record.fsgIndex++ + jumpFsg(record) + workerInitFsgs([record.nodes[record.fsgIndex]]) + } + }} + /> +
+ {value} +
+ } + }, + { + dataIndex: 'count', + title: t('fsg.count'), + width: 90, + ellipsis: true, + sorter: (a: FsgTableRow, b: FsgTableRow): number => a.count - b.count + }, + { + dataIndex: 'nodeCount', + title: t('fsg.nodeCount'), + width: 130, + ellipsis: true, + sorter: (a: FsgTableRow, b: FsgTableRow): number => a.nodeCount - b.nodeCount + }, + { + dataIndex: 'meanDuration', + title: t('fsg.meanDuration'), + width: 155, + ellipsis: true, + sorter: (a: FsgTableRow, b: FsgTableRow): number => a.meanDuration - b.meanDuration, + render: (value) => (value > 0 ? value : 'NA') + }, + { + dataIndex: 'totalDuration', + title: t('fsg.totalDuration'), + width: 150, + ellipsis: true, + sorter: (a: FsgTableRow, b: FsgTableRow): number => a.totalDuration - b.totalDuration, + render: (value) => (value > 0 ? value : 'NA') + }, + { + dataIndex: 'meanMteTime', + title: t('fsg.meanMteTime'), + width: 165, + ellipsis: true, + sorter: (a: FsgTableRow, b: FsgTableRow): number => a.meanMteTime - b.meanMteTime, + render: (value) => (value > 0 ? value : 'NA') + }, + { + dataIndex: 'totalMteTime', + title: t('fsg.totalMteTime'), + width: 160, + ellipsis: true, + sorter: (a: FsgTableRow, b: FsgTableRow): number => a.totalMteTime - b.totalMteTime, + render: (value) => (value > 0 ? value : 'NA') + } + ] +} + +export const FsgTable = () => { + const t = useI18n() + const [loading, setLoading] = useState(false) + const [fsgData, setFsgData] = useState([]) + const [isFullDisplay, setIsFullDisplay] = useState(false) + const [selectedRow, setSelectedRow] = useState() + const currentGraph = useAtomValue(currentGraphAtom) + const path = useAtomValue(modelPathAtom) + + const jumpFsg = (record: FsgTableRow) => { + const { fsgIndex, nodes } = record + const firstNode = data?.nodes.find(node => (nodes[fsgIndex][0] === node.id)) + if (firstNode !== undefined) { + startAnimation({ + x: zoom * (innerWidth / 2 - firstNode.x - firstNode.width / 2), + y: zoom * (innerHeight / 4 - firstNode.y - firstNode.height / 2) + }, 500) + } + } + const columns = getColumns(t, jumpFsg, workerInitFsgs) + const zoom = useAtomValue(zoomAtom) + const startAnimation = useAnimatedTranslate() + const data = useAtomValue(nodesEdgesAtom) + const globalTheme = useAtomValue(themeAtom) + const queryFsg = async (repeat: number, min: number, max: number) => { + setLoading(true) + try { + const res = await invoke( + 'mine_fsg', + { path, minSup: repeat, min, max, name: currentGraph.name } + ) + const fsgs = dedup(sortedFsgs(res)) + setFsgData(fsgs) + fsgDataCach = res + } catch (err) { + message.error({ + content: typeof err === 'string' ? err : JSON.stringify(err), + className: 'dark:text-white dark:[&_div]:!bg-dark-light', + }) + } + setLoading(false) + } + const exportOpDuration = async (path: string): Promise => { + setLoading(true) + try { + const res = await invoke('analyze_duration', { path }) + + opDuration = res + } catch (err) { + message.error({ + content: typeof err === 'string' ? err : JSON.stringify(err), + className: 'dark:text-white dark:[&_div]:!bg-dark-light', + }) + } + const fsgs = dedup(sortedFsgs(fsgDataCach)) + setFsgData(fsgs) + setLoading(false) + } + + const onRow = (record: FsgTableRow) => { + return { + onClick: (e: React.MouseEvent) => { + const isJumpFsgIcon = document.getElementById(`jumpFsg_${record.fsg}`)?.contains(e.target as HTMLElement) + + if (isJumpFsgIcon) { + setSelectedRow(record) + return + } + + if (isEqual(record, selectedRow)) { + setSelectedRow(undefined) + workerToggleFsgs(false) + return + } + setSelectedRow(record) + jumpFsg(record) + workerInitFsgs([record.nodes[record.fsgIndex]]) + } + } + } + + useEffect(() => { + setFsgData([]) + setLoading(false) + setSelectedRow(undefined) + setIsFullDisplay(false) + opDuration = {} + fsgDataCach = [] + }, [path]) + + useEffect(() => { + if (selectedRow === undefined) { + return + } + if (isFullDisplay) { + workerInitFsgs(selectedRow.nodes) + } else { + workerInitFsgs([selectedRow.nodes[selectedRow.fsgIndex]]) + } + }, [isFullDisplay]) + + return <> + + +
record.key === selectedRow?.key ? 'bg-blue-300' : ''} + pagination={{ + className: '!my-2', + pageSizeOptions: ['10', '20', '50', '100'], + total: fsgData.length, + showTotal: (total: number): string => t('fsg.paginationTotal', { total }), + showSizeChanger: true, + showQuickJumper: true + }} + /> + + +} \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx index c88fe73e8..56b4406b2 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx @@ -13,253 +13,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { type I18nFunc, useAnimatedTranslate, useI18n } from 'hooks' -import { useAtomValue } from 'jotai' import { joinCls } from 'libs' -import Filter from './Filter' -import { - modelPathAtom, - fsgPanelVisibleAtom, - themeAtom, - currentGraphAtom, - zoomAtom, - nodesEdgesAtom -} from '../../stores' +import { fsgPanelVisibleAtom } from '../../stores' import { useAtom } from 'jotai/index' -import { invoke } from '@tauri-apps/api/core' -import type { ColumnsType } from 'antd/es/table' -import { ConfigProvider, message, Table, theme } from 'antd' import { useEffect, useRef, useState } from 'react' -import { workerInitFsgs, workerToggleFsgs } from '../../worker-apis' import { Resizer, type ResizerType } from 'ui/Resizer' import Pushpin from "icons/pushpin.svg?react" -import UpIcon from "icons/up.svg?react" -import { isEqual } from 'lodash' - -/** - * The current algorithm is flawed, results returned are duplicated, - * and there are differences only in order - * @param graphs Array of NodeId array - */ -let opDuration: OpDuration = {} -let fsgDataCach: FsgRet[] = [] - -const getOpStructAvgTime = (opStruct: string, type: TimeType): number => { - const opList = opStruct.split(',') - let avgTime = 0 - opList.forEach(op => (avgTime += opDuration[op]?.[type] ?? 0)) - return avgTime -} - -const dedup = (graphs: FsgRet[]): FsgTableRow[] => { - const seen = new Set() - return graphs.map((item, index) => { - const dup_nodes = item.instances.map(i => i.node_ids.map(n => n.nid)) - - const nodes = dup_nodes.filter(arr => { - const sorted = arr.toSorted() - const key = sorted.join('') - return seen.has(key) ? false : seen.add(key) && true - }) - - const meanDuration = getOpStructAvgTime(item.optype_struct, 'avg_duration_us') - const meanMteTime = getOpStructAvgTime(item.optype_struct, 'avg_memory_move_time_us') - - return { - key: `${item.optype_struct}_${index}_${meanDuration}_${meanMteTime}`, - fsg: item.optype_struct, - count: nodes.length, - nodeCount: item.instances[0]?.node_num ?? 0, - meanDuration, - totalDuration: meanDuration * nodes.length, - meanMteTime, - totalMteTime: meanMteTime * nodes.length, - nodes, - fsgIndex: 0, - } - }).filter(item => item.count > 0 && item.nodeCount > 0) -} - -const sortFn = (lhs: FsgRet, rhs: FsgRet) => - (rhs.instances[0]?.node_num ?? 0) - (lhs.instances[0]?.node_num ?? 0) - -const sortedFsgs = (raw: FsgRet[]) => raw.sort(sortFn) - -const getColumns = (t: I18nFunc, jumpFsg: (record: FsgTableRow) => void, workerInitFsgs: (nodes: string[][]) => void): ColumnsType => { - return [ - { - dataIndex: 'fsg', - title: t('fsg.fsg'), - width: 200, - ellipsis: true, - sorter: (a: FsgTableRow, b: FsgTableRow): number => a.fsg.localeCompare(b.fsg), - render: (value, record) => { - return
- -
- { - if (record.fsgIndex > 0) { - record.fsgIndex-- - jumpFsg(record) - workerInitFsgs([record.nodes[record.fsgIndex]]) - } - }} - /> - { - if (record.fsgIndex < record.nodes.length - 1) { - record.fsgIndex++ - jumpFsg(record) - workerInitFsgs([record.nodes[record.fsgIndex]]) - } - }} - /> -
- {value} -
- } - }, - { - dataIndex: 'count', - title: t('fsg.count'), - width: 90, - ellipsis: true, - sorter: (a: FsgTableRow, b: FsgTableRow): number => a.count - b.count - }, - { - dataIndex: 'nodeCount', - title: t('fsg.nodeCount'), - width: 130, - ellipsis: true, - sorter: (a: FsgTableRow, b: FsgTableRow): number => a.nodeCount - b.nodeCount - }, - { - dataIndex: 'meanDuration', - title: t('fsg.meanDuration'), - width: 155, - ellipsis: true, - sorter: (a: FsgTableRow, b: FsgTableRow): number => a.meanDuration - b.meanDuration, - render: (value) => (value > 0 ? value : 'NA') - }, - { - dataIndex: 'totalDuration', - title: t('fsg.totalDuration'), - width: 150, - ellipsis: true, - sorter: (a: FsgTableRow, b: FsgTableRow): number => a.totalDuration - b.totalDuration, - render: (value) => (value > 0 ? value : 'NA') - }, - { - dataIndex: 'meanMteTime', - title: t('fsg.meanMteTime'), - width: 165, - ellipsis: true, - sorter: (a: FsgTableRow, b: FsgTableRow): number => a.meanMteTime - b.meanMteTime, - render: (value) => (value > 0 ? value : 'NA') - }, - { - dataIndex: 'totalMteTime', - title: t('fsg.totalMteTime'), - width: 160, - ellipsis: true, - sorter: (a: FsgTableRow, b: FsgTableRow): number => a.totalMteTime - b.totalMteTime, - render: (value) => (value > 0 ? value : 'NA') - } - ] -} +import { FsgTable } from './FsgTable' const DEFAULT_TOP = 60 const DEFAULT_MARGIN = 80 const Fsg = () => { const [visible, setVisible] = useAtom(fsgPanelVisibleAtom) - const t = useI18n() const containerRef = useRef(null) - const [loading, setLoading] = useState(false) const [width, setWidth] = useState(690) const [top, setTop] = useState(DEFAULT_TOP) const [left, setLeft] = useState(10) const [isLock, setIsLock] = useState(false) - const path = useAtomValue(modelPathAtom) - const [fsgData, setFsgData] = useState([]) - const [selectedRow, setSelectedRow] = useState() - const currentGraph = useAtomValue(currentGraphAtom) - - const jumpFsg = (record: FsgTableRow) => { - const { fsgIndex, nodes } = record - const firstNode = data?.nodes.find(node => (nodes[fsgIndex][0] === node.id)) - if (firstNode !== undefined) { - startAnimation({ - x: zoom * (innerWidth / 2 - firstNode.x - firstNode.width / 2), - y: zoom * (innerHeight / 4 - firstNode.y - firstNode.height / 2) - }, 500) - } - } - const columns = getColumns(t, jumpFsg, workerInitFsgs) - const zoom = useAtomValue(zoomAtom) - const startAnimation = useAnimatedTranslate() - const data = useAtomValue(nodesEdgesAtom) - const globalTheme = useAtomValue(themeAtom) - - const queryFsg = async (repeat: number, min: number, max: number) => { - setLoading(true) - try { - const res = await invoke( - 'mine_fsg', - { path, minSup: repeat, min, max, name: currentGraph.name } - ) - const fsgs = dedup(sortedFsgs(res)) - setFsgData(fsgs) - fsgDataCach = res - } catch (err) { - message.error({ - content: typeof err === 'string' ? err : JSON.stringify(err), - className: 'dark:text-white dark:[&_div]:!bg-dark-light', - }) - } - setLoading(false) - } - const exportOpDuration = async (path: string): Promise => { - setLoading(true) - try { - const res = await invoke('analyze_duration', { path }) - - opDuration = res - } catch (err) { - message.error({ - content: typeof err === 'string' ? err : JSON.stringify(err), - className: 'dark:text-white dark:[&_div]:!bg-dark-light', - }) - } - const fsgs = dedup(sortedFsgs(fsgDataCach)) - setFsgData(fsgs) - setLoading(false) - } - - const onRow = (record: FsgTableRow) => { - return { - onClick: (e: React.MouseEvent) => { - const isJumpFsgIcon = document.getElementById(`jumpFsg_${record.fsg}`)?.contains(e.target as HTMLElement) - - if (isJumpFsgIcon) { - return - } - - if (isEqual(record, selectedRow)) { - setSelectedRow(undefined) - workerToggleFsgs(false) - return - } - setSelectedRow(record) - jumpFsg(record) - workerInitFsgs([record.nodes[record.fsgIndex]]) - } - } - } const changeWidth: ResizerType['callback'] = (value, _) => { if (containerRef.current === null) { @@ -307,14 +79,6 @@ const Fsg = () => { } }, [isLock]) - useEffect(() => { - setFsgData([]); - setLoading(false); - setSelectedRow(undefined); - opDuration = {} - fsgDataCach = [] - }, [path]) - useEffect(() => { moveContainer(0, 0) }, [window.innerHeight, window.innerWidth]) @@ -341,37 +105,7 @@ const Fsg = () => { /> - - -
record.key === selectedRow?.key ? 'bg-blue-300' : ''} - pagination={{ - className: '!my-2', - pageSizeOptions: ['10', '20', '50', '100'], - total: fsgData.length, - showTotal: (total: number): string => t('fsg.paginationTotal', { total }), - showSizeChanger: true, - showQuickJumper: true - }} - /> - + } diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src/locales/en.json b/plugins/mindstudio-insight-plugins/ModelVis/app/src/locales/en.json index b628064a5..41cb2c5f4 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src/locales/en.json +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src/locales/en.json @@ -44,6 +44,7 @@ "invalidQueryWarning": "Invalid Condition: The number of nodes on the left side cannot be greater than that on the right side", "meanMteTime": "Mean Mte Time(us)", "totalMteTime": "Total Mte Time(us)", - "importCSV": "Import CSV" + "importCSV": "Import CSV", + "allStructures": "All Structures" } } \ No newline at end of file diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src/locales/zh-CN.json b/plugins/mindstudio-insight-plugins/ModelVis/app/src/locales/zh-CN.json index e921681d2..0728fc3ab 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src/locales/zh-CN.json +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src/locales/zh-CN.json @@ -44,6 +44,7 @@ "invalidQueryWarning": "无效查询条件:结构中节点数量范围左侧不能大于右侧", "meanMteTime": "Mte平均时间(us)", "totalMteTime": "Mte总时间(us)", - "importCSV": "导入CSV" + "importCSV": "导入CSV", + "allStructures": "全部结构" } } \ No newline at end of file -- Gitee From 0bf451b8c82e8a089c9ab0cd831536aa1270eb59 Mon Sep 17 00:00:00 2001 From: heiheihei <1395202740@qq.com> Date: Sat, 30 Aug 2025 10:42:11 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E9=87=8D=E5=A4=8D=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ModelVis/app/src/ModelStructure/Fsg/index.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx index 56b4406b2..fe7bfeeee 100644 --- a/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx +++ b/plugins/mindstudio-insight-plugins/ModelVis/app/src/ModelStructure/Fsg/index.tsx @@ -80,8 +80,14 @@ const Fsg = () => { }, [isLock]) useEffect(() => { - moveContainer(0, 0) - }, [window.innerHeight, window.innerWidth]) + const handleResize = () => { + moveContainer(0, 0) + }; + window.addEventListener('resize', handleResize) + return () => { + window.removeEventListener('resize', handleResize) + } + }, []) return