diff --git a/skyline-vscode/react-ui/src/App.js b/skyline-vscode/react-ui/src/App.js index e4cfbd0..84c6858 100644 --- a/skyline-vscode/react-ui/src/App.js +++ b/skyline-vscode/react-ui/src/App.js @@ -1,23 +1,22 @@ -import './App.css'; -import './styles.css'; +import "./App.css"; +import "./styles.css"; -import React, { useState, useEffect } from 'react'; -import { Alert, Button, Card, Tab, Tabs, Container } from 'react-bootstrap'; +import React, { useState, useEffect } from "react"; +import { Alert, Button, Card, Tab, Tabs, Container } from "react-bootstrap"; -import ProjectInfo from './components/ProjectInfo' -import Habitat from './sections/Habitat' -import DeploymentTab from './sections/DeploymentTab' -import WelcomeScreen from './sections/WelcomeScreen'; -import PerfBarContainer from './sections/PerfBarContainer'; +import ProjectInfo from "./components/ProjectInfo"; +import Habitat from "./sections/Habitat"; +import DeploymentTab from "./sections/DeploymentTab"; +import WelcomeScreen from "./sections/WelcomeScreen"; +import PerfBarContainer from "./sections/PerfBarContainer"; -import ReactTooltip from 'react-tooltip'; +import ReactTooltip from "react-tooltip"; - -import { computePercentage, getTraceByLevel } from './utils/utils'; -import { profiling_data } from './data/mock_data'; -import EnergyConsumption from './sections/EnergyConsumption'; -import Iterations from './sections/Iterations'; -import MemThroughputContainer from './sections/MemThroughputContainer'; +import { computePercentage, getTraceByLevel } from "./utils/utils"; +import { profiling_data } from "./data/mock_data"; +import EnergyConsumption from "./sections/EnergyConsumption"; +import Iterations from "./sections/Iterations"; +import MemThroughputContainer from "./sections/MemThroughputContainer"; // https://stackoverflow.com/questions/54135313/webview-extension-in-typescript /** @@ -27,189 +26,212 @@ import MemThroughputContainer from './sections/MemThroughputContainer'; * @returns The VSCode API handle */ function acquireApi() { - // if (typeof this.acquireApi.api == 'undefined') { - if (typeof acquireApi.api === 'undefined') { - if (typeof acquireVsCodeApi === "function") { - let f = window['acquireVsCodeApi']; - let a = f(); - acquireApi.api = a; - } else { - acquireApi.api = null; - } + // if (typeof this.acquireApi.api == 'undefined') { + if (typeof acquireApi.api === "undefined") { + if (typeof acquireVsCodeApi === "function") { + let f = window["acquireVsCodeApi"]; + let a = f(); + acquireApi.api = a; + } else { + acquireApi.api = null; } + } - return acquireApi.api; + return acquireApi.api; } function restartProfiling() { - console.log("restartProfiling"); - let vscode = App.vscodeApi; - vscode.postMessage({ - command: "restart_profiling_clicked" - }); + console.log("restartProfiling"); + let vscode = App.vscodeApi; + vscode.postMessage({ + command: "restart_profiling_clicked", + }); } const sendMock = false; function App() { - const [analysisState, setAnalysisState] = useState(); - const [textChanged, setTextChanged] = useState(false); - const [timeBreakDown, setTimeBreakdown] = useState([]); + const [analysisState, setAnalysisState] = useState(); + const [textChanged, setTextChanged] = useState(false); + const [timeBreakDown, setTimeBreakdown] = useState([]); - const [vscodeApi, setVscodeApi] = useState(acquireApi()); - const [errorText, setErrorText] = useState(); - const [connectionStatus, setConnectionStatus] = useState(false); - const [numIterations,setNumIterations] = useState(100000); + const [vscodeApi, setVscodeApi] = useState(acquireApi()); + const [errorText, setErrorText] = useState(); + const [connectionStatus, setConnectionStatus] = useState(false); + const [numIterations, setNumIterations] = useState(100000); - App.vscodeApi = vscodeApi; + App.vscodeApi = vscodeApi; - const resetApp = function() { - setErrorText(""); - setAnalysisState(undefined); - } + const resetApp = function () { + setErrorText(""); + setAnalysisState(undefined); + }; - const connect = function() { - resetApp(); - let vscode = App.vscodeApi; - if(vscode){ - vscode.postMessage({ - command: "connect" - }); - } + const connect = function () { + resetApp(); + let vscode = App.vscodeApi; + if (vscode) { + vscode.postMessage({ + command: "connect", + }); } - - const processAnalysisState = function (state) { - setAnalysisState(state); - if (state.breakdown) { - let operation_tree = state.breakdown.operation_tree; - let { coarse, fine } = getTraceByLevel(operation_tree); - setTimeBreakdown({ - coarse: computePercentage(coarse, state.breakdown.iteration_run_time_ms), - fine: computePercentage(fine, state.breakdown.iteration_run_time_ms) - }); - ReactTooltip.rebuild(); - } + }; + + const processAnalysisState = function (state) { + setAnalysisState(state); + if (state.breakdown) { + let operation_tree = state.breakdown.operation_tree; + let { coarse, fine } = getTraceByLevel(operation_tree); + setTimeBreakdown({ + coarse: computePercentage( + coarse, + state.breakdown.iteration_run_time_ms + ), + fine: computePercentage(fine, state.breakdown.iteration_run_time_ms), + }); + ReactTooltip.rebuild(); } + }; + + useEffect(function () { + window.addEventListener("message", (event) => { + if (event.data["message_type"] === "connection") { + setConnectionStatus(event.data["status"]); + } else if (event.data["message_type"] === "analysis") { + processAnalysisState(event.data); + } else if (event.data["message_type"] === "text_change") { + setTextChanged(true); + } else if (event.data["message_type"] === "error") { + setErrorText(event.data["error_text"]); + } + }); - useEffect(function () { - window.addEventListener('message', event => { - if (event.data['message_type'] === "connection") { - setConnectionStatus(event.data['status']); - } else if (event.data['message_type'] === "analysis") { - processAnalysisState(event.data); - } else if (event.data['message_type'] === "text_change") { - setTextChanged(true); - } else if (event.data['message_type'] === 'error') { - setErrorText(event.data['error_text']); - } - }); - - - - if (sendMock) { - setTimeout(() => { - const mockResponse = profiling_data; - processAnalysisState(mockResponse); - }, 1000); - } - - return() => { - window.removeEventListener("message",()=>{}) //remove event listener before re-render to avoid memory leaks - } - }, []); - - if (!connectionStatus && !sendMock) { - return ( - <> - - Connection Error -

- Connection has been lost to the profiler. Please reconnect the profiler and double check your ports then click connect -

- -
- - ) - } - if (errorText) { - return ( - <> - - Analysis Error -

- An error has occurred during analysis. This could be a problem with Skyline or possibly your code. For more information, refer to the detailed message below: -

-
-

- - {errorText} - -

-
- -
- - ) - } - else if (analysisState && analysisState['throughput'] && Object.keys(analysisState['throughput']).length > 0) - { - return ( - <> - - - Project Information - - - { textChanged && - - Change is detected in the project. - - } - - - -

- - -
-
- { - return { - label: elem["percentage"] > 10 ? elem["name"] : "", - percentage: elem["percentage"], - clickable: false - } - }) : []} - renderPerfBars={timeBreakDown.fine} - /> - -
-
- - - -
-
-
- - - -
-
- - ); - } - else { - return ( - <> - - - ); + if (sendMock) { + setTimeout(() => { + const mockResponse = profiling_data; + processAnalysisState(mockResponse); + }, 1000); } + + return () => { + window.removeEventListener("message", () => {}); //remove event listener before re-render to avoid memory leaks + }; + }, []); + + if (!connectionStatus && !sendMock) { + return ( + <> + + Connection Error +

+ Connection has been lost to the profiler. Please reconnect the + profiler and double check your ports then click connect +

+ +
+ + ); + } + if (errorText) { + return ( + <> + + Analysis Error +

+ An error has occurred during analysis. This could be a problem with + Skyline or possibly your code. For more information, refer to the + detailed message below: +

+
+

+ {errorText} +

+
+ +
+ + ); + } else if ( + analysisState && + analysisState["throughput"] && + Object.keys(analysisState["throughput"]).length > 0 + ) { + return ( + <> + + + Project Information + + + {textChanged && ( + + Change is detected in the project.{" "} + + + )} + + + +

+ + +
+
+ { + return { + label: + elem["percentage"] > 10 ? elem["name"] : "", + percentage: elem["percentage"], + clickable: false, + }; + }) + : [] + } + renderPerfBars={timeBreakDown.fine} + /> + +
+
+ + + +
+
+
+ + + +
+
+ + ); + } else { + return ( + <> + + + ); + } } export default App; diff --git a/skyline-vscode/react-ui/src/components/BarGraph.js b/skyline-vscode/react-ui/src/components/BarGraph.js new file mode 100644 index 0000000..e9c3bca --- /dev/null +++ b/skyline-vscode/react-ui/src/components/BarGraph.js @@ -0,0 +1,49 @@ +import React from "react"; +import { + ResponsiveContainer, + BarChart, + XAxis, + YAxis, + Tooltip, + Bar, + Label, +} from "recharts"; + +export const HorizontalBarGraph = ({ data, height, xlabel, ylabel, color }) => { + let upperLimit = 0; + data.forEach((element) => { + const predictedTime = parseFloat(Number(element.x)); + upperLimit = predictedTime > upperLimit ? predictedTime : upperLimit; + }); + upperLimit *= 1.1; + data.sort((a, b) => a.x - b.x); + return ( + + + + + + + + + ); +}; diff --git a/skyline-vscode/react-ui/src/components/HorizontalBarGraph.js b/skyline-vscode/react-ui/src/components/HorizontalBarGraph.js deleted file mode 100644 index 23176c5..0000000 --- a/skyline-vscode/react-ui/src/components/HorizontalBarGraph.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from "react"; -import { - ResponsiveContainer, - BarChart, - CartesianGrid, - XAxis, - YAxis, - Tooltip, - Bar, -} from "recharts"; - -const BarGraph = ({ data, height, xlabel, ylabel, color }) => { - let upperLimit = 0; - data.forEach(element => { - const predictedTime = parseFloat(Number(element.x)); - upperLimit = predictedTime > upperLimit ? predictedTime: upperLimit - }); - upperLimit *=1.1; - return ( - - - - - - - - - - ); -}; - -export default BarGraph; diff --git a/skyline-vscode/react-ui/src/components/ScatterGraph.js b/skyline-vscode/react-ui/src/components/ScatterGraph.js index 48658d4..b592e36 100644 --- a/skyline-vscode/react-ui/src/components/ScatterGraph.js +++ b/skyline-vscode/react-ui/src/components/ScatterGraph.js @@ -9,23 +9,33 @@ import { ResponsiveContainer, Label, Legend, + Tooltip, + LabelList, } from "recharts"; -import { calculate_training_time,currencyFormat } from "../utils/utils"; +import { calculate_training_time, currencyFormat } from "../utils/utils"; +import { gpuPropertyList } from "../data/providers"; -const ScatterGraph = ({ data, onClickHandler, xlabel, ylabel, providers,numIterations }) => { +export const ProviderScatterGraph = ({ + data, + onClickHandler, + xlabel, + ylabel, + providers, + numIterations, +}) => { const finalData = []; - if (data.length>0){ - data.forEach((item)=>{ + if (data.length > 0) { + data.forEach((item) => { const time = calculate_training_time(numIterations, item); const cost = item.info.cost * time; finalData.push({ ...item, x: time, - y: cost - }) - }) + y: cost, + }); + }); } - + const formatYAxis = (value) => { return currencyFormat(value); }; @@ -33,9 +43,9 @@ const ScatterGraph = ({ data, onClickHandler, xlabel, ylabel, providers,numItera const fortmatXAxis = (value) => { const formatter = new Intl.NumberFormat("en-US", { notation: "compact", - }) + }); return formatter.format(value); - } + }; return ( <> @@ -94,4 +104,147 @@ const ScatterGraph = ({ data, onClickHandler, xlabel, ylabel, providers,numItera ); }; -export default ScatterGraph; +export const HabitatScatterGraph = ({ habitatData, height }) => { + const renderTooltip = (props) => { + const { active, payload } = props; + + if (active && payload && payload.length) { + const data = payload[0] && payload[0].payload; + + return ( +
+

{data.card}

+

+ time: + {data.time} +

+
+ ); + } + + return null; + }; + + const renderCustomizedLabel = (props) => { + const { x, y, width, height, value } = props; + console.log(x, y, width, height, value); + const y_offset = 5; + return ( + + {value} + + ); + }; + + const NUMBER_OF_COLUMNS = 5; + const habitatConsumerCards = []; + const habitatServerCards = []; + const sourceInHabitat = []; + + habitatData.sort((a, b) => a[1] - b[1]); + + habitatData.forEach((habitatItem,idx) => { + const findGPUProperty = gpuPropertyList.find( + (item) => item.name.toLowerCase() === habitatItem[0].toLowerCase() + ); + if (findGPUProperty) { + if (findGPUProperty.type === "server") { + habitatServerCards.push({ + time: parseFloat(Number(habitatItem[1]).toFixed(2)), + card: habitatItem[0], + index: idx%NUMBER_OF_COLUMNS +0.5, + size: 100, + }); + } else { + habitatConsumerCards.push({ + time: parseFloat(Number(habitatItem[1]).toFixed(2)), + card: habitatItem[0], + index: idx%NUMBER_OF_COLUMNS+0.5, + size: 100, + }); + } + } else { + sourceInHabitat.push({ + time: parseFloat(Number(habitatItem[1]).toFixed(2)), + card: "Local GPU", + index: idx%NUMBER_OF_COLUMNS+0.5, + size: 100, + }); + } + }); + console.log(sourceInHabitat) + + const UPPER_LIMIT = + 1.2 * habitatData.reduce((a, b) => Math.max(a, b[1]), -Infinity); + + return ( + <> + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/skyline-vscode/react-ui/src/data/mock_data.js b/skyline-vscode/react-ui/src/data/mock_data.js index 57d6147..7faef0f 100644 --- a/skyline-vscode/react-ui/src/data/mock_data.js +++ b/skyline-vscode/react-ui/src/data/mock_data.js @@ -873,18 +873,18 @@ export const profiling_data = { ], }, habitat: [ - ["source", 22.029312], - ["P100",14.069682], - ["P4000", 127.268085], // 27.268085 - ["RTX2070", 16.088268], - ["RTX2080Ti", 11.826558], - ["T4", 22.029312], - ["V100", 10.182922], - ["A100", 10.068596], - ["RTX3090", 9.841998], - ["A40", 11.558072], - ["A4000", 14.67059], - ["RTX4000", 20.2342], + ["source", 21.767504], + ["P100",13.972405], + ["P4000", 56.889559], // 27.268085 + ["RTX2070", 15.942612], + ["RTX2080Ti", 11.753607], + ["T4", 21.767504], + ["V100", 10.154535], + ["A100", 10.081459], + ["RTX3090", 9.823705], + ["A40", 11.507118], + ["A4000", 14.537657], + ["RTX4000", 20.04405], ], energy: { current: { diff --git a/skyline-vscode/react-ui/src/data/providers.js b/skyline-vscode/react-ui/src/data/providers.js index cb3dbed..d216603 100644 --- a/skyline-vscode/react-ui/src/data/providers.js +++ b/skyline-vscode/react-ui/src/data/providers.js @@ -269,15 +269,15 @@ export const cloudProviders = { } export const gpuPropertyList = [ - { name: "p100", vmem: 16 }, - { name: "p4000", vmem: 8 }, - { name: "rtx2070", vmem: 8 }, - { name: "rtx2080ti", vmem: 11 }, - { name: "t4", vmem: 16 }, - { name: "v100", vmem: 16 }, - { name: "a100", vmem: 40 }, - { name: "rtx3090", vmem: 24}, - { name: "a40", vmem: 48}, - { name: "a4000", vmem: 16}, - { name: "rtx4000", vmem: 8} + { name: "p100", vmem: 16, type: "server" }, + { name: "p4000", vmem: 8, type: "server" }, + { name: "rtx2070", vmem: 8, type: "consumer" }, + { name: "rtx2080ti", vmem: 11, type: "consumer" }, + { name: "t4", vmem: 16, type: "server" }, + { name: "v100", vmem: 16, type: "server" }, + { name: "a100", vmem: 40, type: "server" }, + { name: "rtx3090", vmem: 24, type: "consumer" }, + { name: "a40", vmem: 48, type: "server" }, + { name: "a4000", vmem: 16, type: "server" }, + { name: "rtx4000", vmem: 8, type: "consumer" } ]; diff --git a/skyline-vscode/react-ui/src/sections/Habitat.js b/skyline-vscode/react-ui/src/sections/Habitat.js index 2c9ff37..3e15f12 100644 --- a/skyline-vscode/react-ui/src/sections/Habitat.js +++ b/skyline-vscode/react-ui/src/sections/Habitat.js @@ -1,60 +1,36 @@ +import React from "react"; + import Subheader from "../components/Subheader"; -import { Container, Row, Spinner, Card } from "react-bootstrap"; -import BarGraph from "../components/HorizontalBarGraph"; +import { Container, Row,Spinner, Card } from "react-bootstrap"; +import { HabitatScatterGraph } from "../components/ScatterGraph"; export default function Habitat({ habitatData }) { - // The colors used for the visualization - // The first n-1 colors should follow a blue gradient while the last - // color is used for the current device. - const colors = [ - "#7986cb", - "#5c6bc0", - "#3f51b5", - "#3949ab", - "#303f9f", - "#283593", - "#1a237e", - "#ffc300", - ]; - - let habitatProperties = []; - - for (var i = 0; i < habitatData.length; i++) { - habitatProperties.push({ - y: habitatData[i][0], - x: habitatData[i][1].toFixed(2), - fill: - habitatData[i][0] === "source" - ? colors[colors.length - 1] - : colors[i % (colors.length - 1)], - }); - } return ( -
- Habitat -
- {habitatData.length === 0 && ( - - - - - Loading Habitat - predictions. - - - - - )} - {habitatData !== [] && ( - - )} + <> +
+ Habitat +
+ {habitatData.length === 0 ? ( + + + + + Loading Habitat + predictions. + + + + + ): + ( + + )} +
-
+ ); } diff --git a/skyline-vscode/react-ui/src/sections/Iterations.js b/skyline-vscode/react-ui/src/sections/Iterations.js index 0018160..ad4f1ed 100644 --- a/skyline-vscode/react-ui/src/sections/Iterations.js +++ b/skyline-vscode/react-ui/src/sections/Iterations.js @@ -1,5 +1,5 @@ import React, { useState } from "react"; -import { Button, Form, FloatingLabel } from "react-bootstrap"; +import { Row, Col, Button, Form, FloatingLabel } from "react-bootstrap"; import styled from "styled-components"; import { numberFormat } from "../utils/utils"; @@ -15,13 +15,13 @@ const Iterations = ({ setNumIterations }) => { const name = e.target.name; let value = e.target.value; if (!value) { - setIterations((prevState)=>({ ...prevState, [name]: 0 })); + setIterations((prevState) => ({ ...prevState, [name]: 0 })); return; } const val = parseFloat(value); if (Number.isInteger(val)) { - setIterations((prevState)=>({ ...prevState, [name]: val })); + setIterations((prevState) => ({ ...prevState, [name]: val })); setMessage(null); } else { setMessage("You must only use integer numbers"); @@ -36,14 +36,15 @@ const Iterations = ({ setNumIterations }) => { iterations.iterPerEpoch && Number.isInteger(iterations.iterPerEpoch) ) { - if(iterations.epochs * iterations.iterPerEpoch>=1e21){ - setMessage("The total number of iterations should be less than 1e21"); - } - else{ + if (iterations.epochs * iterations.iterPerEpoch >= 1e21) { + setMessage("The total number of iterations should be less than 1e21"); + } else { setEstimation(true); setNumIterations(iterations.epochs * iterations.iterPerEpoch); setMessage(null); - setTimeout(()=>{setEstimation(false)},2000); + setTimeout(() => { + setEstimation(false); + }, 2000); } } }; @@ -51,63 +52,80 @@ const Iterations = ({ setNumIterations }) => { return ( <> -
-
-
-
Training Schedule
- - - - - - - {message && ( - - {message} - - )} - - - Total number of iterations:{" "} - {numberFormat(iterations.epochs * iterations.iterPerEpoch)} - - - - - -
+
+
+
+ +
Training Schedule
+ + + + + + + + + + + + + + + Total number of iterations:{" "} + {numberFormat( + iterations.epochs * iterations.iterPerEpoch + )} + + + {message && ( + + {message} + + )} + +
+ + + + + + + +
+
-
); }; const Wrapper = styled.main` - .warning-message{ + .warning-message { font-size: 14; font-weight: 700; color: "red"; - } - .iterations-text{ + .iterations-text { font-size: 14; font-weight: 700; } diff --git a/skyline-vscode/react-ui/src/sections/ProviderPanel.js b/skyline-vscode/react-ui/src/sections/ProviderPanel.js index c868e42..cdb138b 100644 --- a/skyline-vscode/react-ui/src/sections/ProviderPanel.js +++ b/skyline-vscode/react-ui/src/sections/ProviderPanel.js @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; import Subheader from "../components/Subheader"; -import ScatterGraph from "../components/ScatterGraph"; +import {ProviderScatterGraph} from "../components/ScatterGraph"; import { Badge, ButtonGroup, @@ -243,7 +243,7 @@ const ProviderPanel = ({ numIterations, habitatData }) => { -