├── cli ├── skyline │ ├── io │ │ ├── __init__.py │ │ ├── sentinel.py │ │ ├── connection_manager.py │ │ └── connection_acceptor.py │ ├── analysis │ │ ├── __init__.py │ │ ├── runner.py │ │ └── static.py │ ├── commands │ │ ├── __init__.py │ │ ├── memory.py │ │ ├── time.py │ │ └── prediction_models.py │ ├── models │ │ ├── __init__.py │ │ └── source_map.py │ ├── protocol │ │ ├── __init__.py │ │ └── message_sender.py │ ├── tests │ │ └── __init__.py │ ├── tracking │ │ ├── __init__.py │ │ ├── time │ │ │ ├── __init__.py │ │ │ ├── report_queries.py │ │ │ ├── report.py │ │ │ └── operation.py │ │ ├── memory │ │ │ ├── __init__.py │ │ │ └── weights.py │ │ ├── utils.py │ │ ├── backward_interceptor.py │ │ ├── base.py │ │ ├── call_stack.py │ │ └── hook_manager.py │ ├── protocol_gen │ │ └── __init__.py │ ├── data │ │ ├── __init__.py │ │ └── hints.yml │ ├── __init__.py │ ├── nvml.py │ ├── evaluate.py │ ├── error_printing.py │ ├── __main__.py │ ├── config │ │ └── __init__.py │ ├── profiler │ │ └── __init__.py │ ├── version_utils.py │ ├── lru_cache.py │ └── user_code_utils.py ├── pyproject.toml ├── dev-setup.sh └── README.md ├── website ├── static │ ├── .nojekyll │ └── img │ │ ├── skyline64x64.png │ │ ├── skyline_dark.png │ │ ├── skyline_light.png │ │ ├── skyline_social.png │ │ └── skyline.svg ├── babel.config.js ├── .gitignore ├── sidebars.js ├── src │ ├── components │ │ ├── styles.module.css │ │ ├── CitationBlock.js │ │ ├── VideoOverlay.js │ │ └── PaperDetails.js │ ├── css │ │ └── custom.css │ └── pages │ │ └── styles.module.css ├── docs │ ├── research.md │ ├── providers.md │ ├── misc.md │ ├── settings.md │ ├── standalone.md │ ├── install.md │ ├── run-time-report.md │ └── intro.md ├── package.json ├── README.md └── docusaurus.config.js ├── plugin ├── .gitignore ├── lib │ ├── env.json │ ├── models │ │ ├── PerfHintState.js │ │ ├── RunTimeEntryLabel.js │ │ ├── MemoryEntryLabel.js │ │ ├── AppState.js │ │ ├── LinearModel.js │ │ ├── Throughput.js │ │ ├── PerfVisState.js │ │ └── Memory.js │ ├── telemetry │ │ ├── react_context.js │ │ ├── EventType.js │ │ ├── client.js │ │ └── events.js │ ├── redux │ │ ├── actions │ │ │ ├── app.js │ │ │ ├── config.js │ │ │ ├── utils.js │ │ │ ├── project.js │ │ │ └── connection.js │ │ ├── store.js │ │ ├── reducers │ │ │ ├── app.js │ │ │ ├── config.js │ │ │ ├── state_transition.js │ │ │ ├── project.js │ │ │ ├── initial_state.js │ │ │ ├── connection.js │ │ │ └── skyline.js │ │ ├── views │ │ │ └── connection_state.js │ │ └── middlewares │ │ │ └── undo_predictions.js │ ├── logger.js │ ├── components │ │ ├── Subheader.js │ │ ├── UsageHighlight.js │ │ ├── NumericDisplay.js │ │ ├── ErrorMessage.js │ │ ├── BarSlider.js │ │ ├── generic │ │ │ ├── InlineHighlight.js │ │ │ └── PerfBarContainer.js │ │ ├── Resizable.js │ │ ├── PerfVis.js │ │ ├── ContextHighlightManager.js │ │ ├── PerfHint.js │ │ └── Memory.js │ ├── config │ │ ├── schema.js │ │ └── manager.js │ ├── logger_impl.js │ ├── io │ │ └── message_sender.js │ ├── skyline.js │ ├── utils.js │ ├── editor │ │ ├── marker.js │ │ └── innpv_file_tracker.js │ └── skyline_session.js ├── styles │ ├── Memory.less │ ├── PerfVis.less │ ├── Throughput.less │ ├── innpv-animation.less │ ├── Elastic.less │ ├── Subheader.less │ ├── NumericDisplay.less │ ├── ErrorMessage.less │ ├── PerfVisStatusBar.less │ ├── PerfHint.less │ ├── GetStarted.less │ ├── BarSlider.less │ ├── PerfBar.less │ ├── hover.less │ ├── PerfVisMainView.less │ └── PerfBarContainer.less ├── menus │ └── skyline.json ├── atom-dev-setup.sh ├── README.md └── package.json ├── .gitattributes ├── assets └── skyline-wordmark.png ├── samples ├── transformer │ └── transformer │ │ ├── Constants.py │ │ ├── __init__.py │ │ ├── README.md │ │ ├── Modules.py │ │ ├── LICENSE │ │ ├── Optim.py │ │ ├── Layers.py │ │ └── SubLayers.py ├── vgg │ ├── entry_point.py │ └── LICENSE ├── densenet │ ├── entry_point.py │ └── LICENSE ├── gnmt │ ├── README.md │ └── seq2seq │ │ ├── train │ │ └── smoothing.py │ │ ├── LICENSE │ │ ├── data │ │ └── config.py │ │ └── models │ │ ├── gnmt.py │ │ └── seq2seq_base.py ├── resnet │ ├── entry_point_resnext.py │ ├── entry_point.py │ └── LICENSE ├── testnet │ ├── entry_point.py │ └── testnet1.py └── legacy │ ├── lenet.py │ ├── testnet2.py │ └── vgg11.py ├── experimental ├── README.md └── components │ ├── XDevice.less │ └── XDevice.js ├── .gitignore ├── tools ├── setup-cli-release-env.sh ├── shared.sh └── release-cli.sh ├── protocol └── Makefile ├── CITATION.cff └── NOTICE /cli/skyline/io/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cli/skyline/analysis/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cli/skyline/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cli/skyline/models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cli/skyline/protocol/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cli/skyline/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cli/skyline/tracking/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cli/skyline/protocol_gen/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cli/skyline/tracking/time/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cli/skyline/tracking/memory/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /plugin/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | node_modules 4 | -------------------------------------------------------------------------------- /plugin/lib/env.json: -------------------------------------------------------------------------------- 1 | { 2 | "development": true 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | assets/* linguist-documentation 2 | website/* linguist-documentation 3 | -------------------------------------------------------------------------------- /plugin/styles/Memory.less: -------------------------------------------------------------------------------- 1 | .innpv-memory { 2 | flex-grow: 1; 3 | flex-basis: 0; 4 | } 5 | -------------------------------------------------------------------------------- /plugin/styles/PerfVis.less: -------------------------------------------------------------------------------- 1 | .innpv-wrap { 2 | height: 100%; 3 | width: 350px; 4 | } 5 | -------------------------------------------------------------------------------- /plugin/styles/Throughput.less: -------------------------------------------------------------------------------- 1 | .innpv-throughput { 2 | flex-grow: 1; 3 | flex-basis: 0; 4 | } 5 | -------------------------------------------------------------------------------- /assets/skyline-wordmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skylineprof/skyline/HEAD/assets/skyline-wordmark.png -------------------------------------------------------------------------------- /cli/pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools>=40.6.0", "wheel"] 3 | build-backend = "setuptools.build_meta" 4 | -------------------------------------------------------------------------------- /website/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /website/static/img/skyline64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skylineprof/skyline/HEAD/website/static/img/skyline64x64.png -------------------------------------------------------------------------------- /website/static/img/skyline_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skylineprof/skyline/HEAD/website/static/img/skyline_dark.png -------------------------------------------------------------------------------- /website/static/img/skyline_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skylineprof/skyline/HEAD/website/static/img/skyline_light.png -------------------------------------------------------------------------------- /website/static/img/skyline_social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skylineprof/skyline/HEAD/website/static/img/skyline_social.png -------------------------------------------------------------------------------- /plugin/lib/models/PerfHintState.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | export default { 4 | NONE: 'none', 5 | INCREASING: 'increasing', 6 | DECREASING: 'decreasing', 7 | }; 8 | -------------------------------------------------------------------------------- /plugin/lib/models/RunTimeEntryLabel.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | export default { 4 | ForwardBackward: 'Forward and Backward', 5 | Untracked: 'Untracked', 6 | }; 7 | -------------------------------------------------------------------------------- /plugin/lib/models/MemoryEntryLabel.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | export default { 4 | Weights: 'Weights', 5 | Activations: 'Activations', 6 | Untracked: 'Untracked', 7 | }; 8 | -------------------------------------------------------------------------------- /cli/skyline/data/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | _DATA_PATH = os.path.abspath(os.path.dirname(__file__)) 4 | 5 | 6 | def get_absolute_path(data_file): 7 | return os.path.join(_DATA_PATH, data_file) 8 | -------------------------------------------------------------------------------- /plugin/lib/telemetry/react_context.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import React from 'react'; 4 | 5 | const TelemetryClientContext = React.createContext(null); 6 | 7 | export default TelemetryClientContext; 8 | -------------------------------------------------------------------------------- /samples/transformer/transformer/Constants.py: -------------------------------------------------------------------------------- 1 | 2 | PAD = 0 3 | UNK = 1 4 | BOS = 2 5 | EOS = 3 6 | 7 | PAD_WORD = '' 8 | UNK_WORD = '' 9 | BOS_WORD = '' 10 | EOS_WORD = '' 11 | -------------------------------------------------------------------------------- /plugin/styles/innpv-animation.less: -------------------------------------------------------------------------------- 1 | @perfbar-duration: 0.2s; 2 | @perfbar-easing: ease-out; 3 | 4 | @noevents-duration: 0.3s; 5 | 6 | @perfbar-label-duration: @perfbar-duration; 7 | @perfbar-label-easing: ease; 8 | -------------------------------------------------------------------------------- /experimental/README.md: -------------------------------------------------------------------------------- 1 | Experimental Code 2 | ================= 3 | 4 | This directory contains experimental scripts and code snippets that are used to 5 | experiment with various approaches to implementing different features in Skyline. 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | 4 | __pycache__ 5 | *.pyc 6 | 7 | # User-specific CLI development environment files 8 | cli/env 9 | cli/skyline_cli.egg-info 10 | 11 | # Build files 12 | cli/build 13 | cli/dist 14 | tools/renv 15 | -------------------------------------------------------------------------------- /plugin/styles/Elastic.less: -------------------------------------------------------------------------------- 1 | @import "innpv-animation"; 2 | 3 | .innpv-elastic-inner { 4 | height: 100%; 5 | transition-property: padding-top, padding-bottom; 6 | transition-duration: @perfbar-duration; 7 | transition-timing-function: @perfbar-easing; 8 | } 9 | -------------------------------------------------------------------------------- /cli/skyline/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.5.0" 2 | 3 | __description__ = "Interactive in-editor performance profiling, visualization, and debugging for PyTorch neural networks." 4 | 5 | __author__ = "Geoffrey Yu" 6 | __email__ = "gxyu@cs.toronto.edu" 7 | 8 | __license__ = "Apache-2.0" 9 | -------------------------------------------------------------------------------- /plugin/lib/redux/actions/app.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import {emptyFor} from './utils'; 4 | import { 5 | APP_OPENED, 6 | APP_CLOSED, 7 | } from './types'; 8 | 9 | export default { 10 | appOpened: emptyFor(APP_OPENED), 11 | appClosed: emptyFor(APP_CLOSED), 12 | }; 13 | 14 | -------------------------------------------------------------------------------- /tools/setup-cli-release-env.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | set -e 4 | virtualenv -p $(which python3) renv 5 | source renv/bin/activate 6 | pip3 install pep517 twine 7 | 8 | echo "" 9 | echo "✓ Done!" 10 | echo "NOTE: Before releasing, please ensure that your ~/.pypirc is correct." 11 | -------------------------------------------------------------------------------- /plugin/lib/logger.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import {Logger, LogLevel} from './logger_impl'; 4 | import env from './env.json'; 5 | 6 | let logger; 7 | 8 | if (env.development) { 9 | logger = new Logger(LogLevel.DEBUG); 10 | } else { 11 | logger = new Logger(LogLevel.WARN); 12 | } 13 | 14 | export default logger; 15 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /plugin/styles/Subheader.less: -------------------------------------------------------------------------------- 1 | @import "ui-variables"; 2 | 3 | .innpv-subheader { 4 | height: 2.5em; 5 | line-height: 2.5em; 6 | color: @text-color-subtle; 7 | font-weight: bold; 8 | border-bottom: 1px solid @inset-panel-border-color; 9 | padding: 0 10px; 10 | 11 | .icon::before { 12 | font-size: 1em; 13 | margin-right: 1px; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /plugin/menus/skyline.json: -------------------------------------------------------------------------------- 1 | { 2 | "menu": [ 3 | { 4 | "label": "Packages", 5 | "submenu": [ 6 | { 7 | "label": "Skyline", 8 | "submenu": [ 9 | { 10 | "label": "Show/Hide Skyline", 11 | "command": "skyline:toggle" 12 | } 13 | ] 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /plugin/styles/NumericDisplay.less: -------------------------------------------------------------------------------- 1 | .innpv-numericdisplay { 2 | text-align: center; 3 | } 4 | 5 | .innpv-numericdisplay-number { 6 | font-size: 2em; 7 | margin: 5px 0; 8 | } 9 | 10 | .innpv-numericdisplay-top { 11 | font-variant: small-caps; 12 | font-weight: bold; 13 | } 14 | 15 | .innpv-numericdisplay-top, 16 | .innpv-numericdisplay-bottom { 17 | font-size: 1em; 18 | } 19 | -------------------------------------------------------------------------------- /plugin/lib/redux/actions/config.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import {fromPayloadCreator} from './utils'; 4 | import { 5 | CONFIG_CHANGED, 6 | } from './types'; 7 | 8 | export default { 9 | // The payload contains the new value of the configuration setting 10 | // (the key refers to the configuration key). 11 | configChanged: fromPayloadCreator(CONFIG_CHANGED, (payload) => payload), 12 | }; 13 | 14 | -------------------------------------------------------------------------------- /plugin/lib/components/Subheader.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import React from 'react'; 4 | 5 | export default class Subheader extends React.Component { 6 | render() { 7 | const {icon, children} = this.props; 8 | return ( 9 |
10 | 11 | {this.props.children} 12 |
13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /plugin/lib/components/UsageHighlight.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import React from 'react'; 4 | 5 | import InlineHighlight from './generic/InlineHighlight'; 6 | 7 | const DECORATIONS = [ 8 | {type: 'line', class: 'innpv-line-highlight'}, 9 | ]; 10 | 11 | export default function UsageHighlight(props) { 12 | return ( 13 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /plugin/lib/redux/actions/utils.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | export function emptyFor(actionType) { 4 | return function() { 5 | return { 6 | type: actionType, 7 | payload: {}, 8 | }; 9 | }; 10 | } 11 | 12 | export function fromPayloadCreator(actionType, payloadCreator) { 13 | return function(...args) { 14 | return { 15 | type: actionType, 16 | payload: payloadCreator(...args), 17 | }; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /website/sidebars.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | sidebar: { 3 | 'About': [ 4 | 'intro', 5 | 'research', 6 | 'misc', 7 | ], 8 | 'Using Skyline': [ 9 | 'install', 10 | 'getting-started', 11 | 'providers', 12 | 'remote', 13 | 'standalone', 14 | ], 15 | 'Reference': [ 16 | 'cli', 17 | 'settings', 18 | 'run-time-report', 19 | 'memory-report', 20 | ], 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /cli/skyline/tracking/utils.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import torch 4 | 5 | DUNDER_REGEX = re.compile('__(?P.+)__') 6 | 7 | 8 | def tensor_size_bytes(tensor): 9 | if tensor is None or not tensor.is_cuda: 10 | return 0 11 | return tensor.numel() * tensor.element_size() 12 | 13 | 14 | def remove_dunder(fn_name): 15 | match = DUNDER_REGEX.match(fn_name) 16 | if match is None: 17 | return fn_name 18 | return match.group('name') 19 | -------------------------------------------------------------------------------- /plugin/lib/redux/store.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import {createStore, applyMiddleware} from 'redux'; 4 | import thunk from 'redux-thunk'; 5 | import undoPredictions from './middlewares/undo_predictions'; 6 | import rootReducer from './reducers/skyline'; 7 | 8 | // We want to avoid using the singleton pattern. Therefore 9 | // we export a factory function instead. 10 | export default function() { 11 | return createStore(rootReducer, applyMiddleware(thunk, undoPredictions)); 12 | } 13 | -------------------------------------------------------------------------------- /samples/transformer/transformer/__init__.py: -------------------------------------------------------------------------------- 1 | import transformer.Constants 2 | import transformer.Modules 3 | import transformer.Layers 4 | import transformer.SubLayers 5 | import transformer.Models 6 | import transformer.Translator 7 | import transformer.Beam 8 | import transformer.Optim 9 | 10 | __all__ = [ 11 | transformer.Constants, transformer.Modules, transformer.Layers, 12 | transformer.SubLayers, transformer.Models, transformer.Optim, 13 | transformer.Translator, transformer.Beam] 14 | -------------------------------------------------------------------------------- /plugin/lib/telemetry/EventType.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | export default class EventType { 4 | constructor({category, action}) { 5 | this._category = category; 6 | this._action = action; 7 | } 8 | 9 | static of(category, action) { 10 | return new EventType({category, action}); 11 | } 12 | 13 | get category() { 14 | return this._category; 15 | } 16 | 17 | get action() { 18 | return this._action; 19 | } 20 | 21 | get name() { 22 | return `${this.category} / ${this.action}`; 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /plugin/lib/redux/reducers/app.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import {APP_OPENED, APP_CLOSED} from '../actions/types'; 4 | import AppState from '../../models/AppState'; 5 | import initialState from './initial_state'; 6 | 7 | export default function(state, action) { 8 | switch (action.type) { 9 | case APP_OPENED: 10 | return { 11 | ...state, 12 | appState: AppState.OPENED, 13 | }; 14 | 15 | case APP_CLOSED: 16 | return initialState; 17 | 18 | default: 19 | return state; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /website/src/components/styles.module.css: -------------------------------------------------------------------------------- 1 | .paperDetails { 2 | margin-bottom: 30px; 3 | } 4 | 5 | .paperDetails a, 6 | .paperDetails button { 7 | margin: 0 20px 20px 0; 8 | } 9 | 10 | .videoOverlay { 11 | position: fixed; 12 | top: 0; 13 | left: 0; 14 | width: 100%; 15 | height: 100%; 16 | z-index: 1000; 17 | backdrop-filter: blur(3px); 18 | background-color: rgba(0, 0, 0, 0.7); 19 | 20 | display: flex; 21 | justify-content: center; 22 | align-items: center; 23 | 24 | transition: opacity 0.3s; 25 | opacity: 0; 26 | } 27 | -------------------------------------------------------------------------------- /cli/skyline/nvml.py: -------------------------------------------------------------------------------- 1 | import pynvml 2 | 3 | 4 | class NVML: 5 | def __enter__(self): 6 | self.start() 7 | return self 8 | 9 | def __exit__(self, exc_type, exc_value, traceback): 10 | self.stop() 11 | 12 | def start(self): 13 | pynvml.nvmlInit() 14 | 15 | def stop(self): 16 | pynvml.nvmlShutdown() 17 | 18 | def get_memory_capacity(self): 19 | # TODO: Support multiple devices 20 | handle = pynvml.nvmlDeviceGetHandleByIndex(0) 21 | return pynvml.nvmlDeviceGetMemoryInfo(handle) 22 | -------------------------------------------------------------------------------- /website/docs/research.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: research 3 | title: Research Paper 4 | --- 5 | 6 | Skyline began as a research project at the [University of 7 | Toronto](https://web.cs.toronto.edu). If you are interested, you can read our 8 | [academic research paper](/paper), which will appear at the 33rd Symposium on 9 | User Interface Software and Technology 10 | [(UIST'20)](https://uist.acm.org/uist2020). 11 | 12 | If you use Skyline in your research and find it useful, please consider citing 13 | our paper. 14 | 15 | import CitationBlock from '../src/components/CitationBlock'; 16 | 17 | 18 | -------------------------------------------------------------------------------- /plugin/lib/redux/actions/project.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import {emptyFor, fromPayloadCreator} from './utils'; 4 | import { 5 | PROJECT_MODIFIED_CHANGE, 6 | PROJECT_EDITORS_CHANGE, 7 | PROJECT_CAN_PROFILE, 8 | } from './types'; 9 | 10 | export default { 11 | modifiedChange: fromPayloadCreator( 12 | PROJECT_MODIFIED_CHANGE, 13 | ({modifiedEditorsByFilePath}) => ({modifiedEditorsByFilePath}), 14 | ), 15 | editorsChange: fromPayloadCreator( 16 | PROJECT_EDITORS_CHANGE, 17 | ({editorsByPath}) => ({editorsByPath}), 18 | ), 19 | canProfile: emptyFor(PROJECT_CAN_PROFILE), 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /plugin/lib/models/AppState.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | export default { 4 | // When the plugin is activated by Atom but the user has not "opened" it 5 | // by running the Skyline:Open command. 6 | ACTIVATED: 'activated', 7 | 8 | // When the plugin has been opened by the user but they have not clicked 9 | // the "Get Started" button. 10 | OPENED: 'opened', 11 | 12 | // When the plugin is performing set up: connecting to the server, binding 13 | // to the text buffer (and creating one if needed), etc. 14 | CONNECTING: 'connecting', 15 | 16 | // When the plugin is operating normally. 17 | CONNECTED: 'connected', 18 | }; 19 | -------------------------------------------------------------------------------- /protocol/Makefile: -------------------------------------------------------------------------------- 1 | GEN_DIR = protocol_gen 2 | 3 | PLUGIN_GEN = ../plugin/lib/$(GEN_DIR) 4 | PLUGIN_GEN_FILE = $(PLUGIN_GEN)/innpv_pb.js 5 | 6 | SERVER_GEN = ../cli/skyline/$(GEN_DIR) 7 | SERVER_GEN_FILE = $(SERVER_GEN)/innpv_pb2.py 8 | 9 | PROTO_FILE = innpv.proto 10 | 11 | .PHONY: all clean 12 | 13 | all: $(PLUGIN_GEN_FILE) $(SERVER_GEN_FILE) 14 | 15 | $(PLUGIN_GEN_FILE): $(PROTO_FILE) 16 | mkdir -p $(PLUGIN_GEN) 17 | protoc --js_out=import_style=commonjs,binary:$(PLUGIN_GEN) $^ 18 | 19 | $(SERVER_GEN_FILE): $(PROTO_FILE) 20 | mkdir -p $(SERVER_GEN) 21 | protoc --python_out=$(SERVER_GEN) $^ 22 | 23 | clean: 24 | rm -rf $(PLUGIN_GEN) $(SERVER_GEN) 25 | -------------------------------------------------------------------------------- /plugin/styles/ErrorMessage.less: -------------------------------------------------------------------------------- 1 | @import "ui-variables"; 2 | @import "innpv-animation"; 3 | 4 | .innpv-error { 5 | display: flex; 6 | height: 100%; 7 | width: 100%; 8 | justify-content: center; 9 | align-items: center; 10 | padding: @component-padding; 11 | transition-property: opacity; 12 | transition-duration: @noevents-duration; 13 | } 14 | 15 | .innpv-error-inner { 16 | h1 { 17 | margin: 0; 18 | margin-bottom: @component-padding; 19 | font-size: 1.5em; 20 | font-weight: bold; 21 | text-align: center; 22 | } 23 | 24 | p { 25 | margin: 0; 26 | margin-bottom: @component-padding; 27 | font-size: 1em; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /plugin/lib/models/LinearModel.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | export default class LinearModel { 4 | constructor({slope, bias}) { 5 | this._slope = slope; 6 | this._bias = bias; 7 | } 8 | 9 | get slope() { 10 | return this._slope; 11 | } 12 | 13 | get bias() { 14 | return this._bias; 15 | } 16 | 17 | evaluate(x) { 18 | return this._slope * x + this._bias; 19 | } 20 | 21 | static fromProtobuf(protobufLinearModel) { 22 | if (protobufLinearModel == null) { 23 | return null; 24 | } 25 | return new LinearModel({ 26 | slope: protobufLinearModel.getSlope(), 27 | bias: protobufLinearModel.getBias(), 28 | }); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /samples/vgg/entry_point.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | import vgg 5 | 6 | 7 | def skyline_model_provider(): 8 | return vgg.vgg11().cuda() 9 | 10 | 11 | def skyline_input_provider(batch_size=16): 12 | return ( 13 | torch.randn((batch_size, 3, 224, 224)).cuda(), 14 | torch.randint(low=0, high=1000, size=(batch_size,)).cuda(), 15 | ) 16 | 17 | 18 | def skyline_iteration_provider(model): 19 | optimizer = torch.optim.SGD(model.parameters(), lr=1e-3) 20 | def iteration(*inputs): 21 | optimizer.zero_grad() 22 | out = model(*inputs) 23 | out.backward() 24 | optimizer.step() 25 | return iteration 26 | -------------------------------------------------------------------------------- /samples/densenet/entry_point.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | import densenet 5 | 6 | 7 | def skyline_model_provider(): 8 | return densenet.densenet121().cuda() 9 | 10 | 11 | def skyline_input_provider(batch_size=16): 12 | return ( 13 | torch.randn((batch_size, 3, 224, 224)).cuda(), 14 | torch.randint(low=0, high=1000, size=(batch_size,)).cuda(), 15 | ) 16 | 17 | 18 | def skyline_iteration_provider(model): 19 | optimizer = torch.optim.SGD(model.parameters(), lr=1e-3) 20 | def iteration(*inputs): 21 | optimizer.zero_grad() 22 | out = model(*inputs) 23 | out.backward() 24 | optimizer.step() 25 | return iteration 26 | -------------------------------------------------------------------------------- /samples/gnmt/README.md: -------------------------------------------------------------------------------- 1 | # GNMT (Google Neural Machine Translation) Model 2 | 3 | This directory contains an implementation of GNMT that was adapted from the 4 | code found in the [MLPerf training repository](https://github.com/mlperf/training/tree/master/rnn_translator). 5 | 6 | To launch an interactive Skyline profiling session for GNMT, run 7 | ``` 8 | skyline interactive entry_point.py 9 | ``` 10 | 11 | 12 | ## License 13 | 14 | This code, with the exception of the `skyline_` prefixed functions in 15 | `entry_point.py`, was adapted from the MLPerf training benchmarks and therefore 16 | shares the same license. The unmodified license can be found in the `LICENSE` 17 | file in the `seq2seq` directory. 18 | -------------------------------------------------------------------------------- /samples/resnet/entry_point_resnext.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | import resnet 5 | 6 | 7 | def skyline_model_provider(): 8 | return resnet.resnext50_32x4d().cuda() 9 | 10 | 11 | def skyline_input_provider(batch_size=16): 12 | return ( 13 | torch.randn((batch_size, 3, 224, 224)).cuda(), 14 | torch.randint(low=0, high=1000, size=(batch_size,)).cuda(), 15 | ) 16 | 17 | 18 | def skyline_iteration_provider(model): 19 | optimizer = torch.optim.SGD(model.parameters(), lr=1e-3) 20 | def iteration(*inputs): 21 | optimizer.zero_grad() 22 | out = model(*inputs) 23 | out.backward() 24 | optimizer.step() 25 | return iteration 26 | -------------------------------------------------------------------------------- /samples/transformer/transformer/README.md: -------------------------------------------------------------------------------- 1 | Transformer Model (Attention is All You Need) 2 | ============================================= 3 | This directory contains a PyTorch implementation of the Transformer model 4 | described in the "[Attention is All You Need](https://arxiv.org/abs/1706.03762)" 5 | paper. This code was adapted from Yu-Hsiang Huang's implementation found in 6 | [jadore801120/attention-is-all-you-need-pytorch](https://github.com/jadore801120/attention-is-all-you-need-pytorch). 7 | 8 | License 9 | ------- 10 | The code inside this directory is adapted from Yu-Hsiang Huang's implementation 11 | and therefore shares the same license. The unmodified license can be found in 12 | the `LICENSE` file. 13 | -------------------------------------------------------------------------------- /plugin/atom-dev-setup.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | SCRIPTPATH=$( cd $(dirname $0) ; pwd -P ) 4 | cd $SCRIPTPATH 5 | 6 | UNINSTALL_OPTION="--uninstall" 7 | HELP_OPTION="--help" 8 | 9 | function install() { 10 | apm link . 11 | } 12 | 13 | function uninstall() { 14 | apm unlink . 15 | } 16 | 17 | if [ "$1" == "$HELP_OPTION" ]; then 18 | echo "Usage: $0 [--uninstall]" 19 | echo "" 20 | echo "This script installs (or uninstalls) a development version of the " 21 | echo "Skyline Atom plugin." 22 | echo "" 23 | echo "Use the --uninstall flag to uninstall the development version of the " 24 | echo "plugin." 25 | 26 | elif [ "$1" == "$UNINSTALL_OPTION" ]; then 27 | uninstall 28 | else 29 | install 30 | fi 31 | -------------------------------------------------------------------------------- /plugin/lib/redux/reducers/config.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import { 4 | getActionNamespace, 5 | isConfigAction, 6 | } from '../actions/types'; 7 | 8 | import configSchema from '../../config/schema'; 9 | 10 | const defaultConfig = (() => { 11 | const config = {}; 12 | for (const key in configSchema) { 13 | if (!Object.prototype.hasOwnProperty.call(configSchema, key)) { 14 | continue; 15 | } 16 | config[key] = configSchema[key].default; 17 | } 18 | return config; 19 | })(); 20 | 21 | export default function(config = defaultConfig, action) { 22 | if (!isConfigAction(getActionNamespace(action))) { 23 | return config; 24 | } 25 | return { 26 | ...config, 27 | ...action.payload, 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /plugin/lib/redux/actions/connection.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import {emptyFor, fromPayloadCreator} from './utils'; 4 | import { 5 | CONN_CONNECTING, 6 | CONN_INITIALIZING, 7 | CONN_INITIALIZED, 8 | CONN_ERROR, 9 | CONN_LOST, 10 | CONN_INCR_SEQ, 11 | } from './types'; 12 | 13 | export default { 14 | connecting: emptyFor(CONN_CONNECTING), 15 | initializing: fromPayloadCreator(CONN_INITIALIZING, ({onTimeout}) => ({onTimeout})), 16 | initialized: fromPayloadCreator(CONN_INITIALIZED, ({projectRoot}) => ({projectRoot})), 17 | error: fromPayloadCreator(CONN_ERROR, ({errorMessage}) => ({errorMessage})), 18 | lost: fromPayloadCreator(CONN_LOST, ({errorMessage}) => ({errorMessage})), 19 | incrementSequence: emptyFor(CONN_INCR_SEQ), 20 | }; 21 | -------------------------------------------------------------------------------- /cli/skyline/evaluate.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import enum 3 | import sys 4 | 5 | import skyline.commands.measurements 6 | import skyline.commands.prediction_models 7 | 8 | 9 | def main(): 10 | parser = argparse.ArgumentParser( 11 | prog="skyline-evaluate", 12 | description="Skyline Evaluation Tool", 13 | ) 14 | subparsers = parser.add_subparsers(title="Commands") 15 | skyline.commands.measurements.register_command(subparsers) 16 | skyline.commands.prediction_models.register_command(subparsers) 17 | args = parser.parse_args() 18 | 19 | if 'func' not in args: 20 | parser.print_help() 21 | sys.exit(1) 22 | 23 | # Run the specified command 24 | args.func(args) 25 | 26 | 27 | if __name__ == '__main__': 28 | main() 29 | -------------------------------------------------------------------------------- /cli/skyline/data/hints.yml: -------------------------------------------------------------------------------- 1 | Conv2d: 2 | in_channels: 3 | effectiveness: 'high' 4 | natural_direction: true 5 | 6 | out_channels: 7 | effectiveness: 'high' 8 | natural_direction: true 9 | 10 | kernel_size: 11 | effectiveness: 'low' 12 | natural_direction: true 13 | 14 | 15 | Linear: 16 | in_features: 17 | effectiveness: 'high' 18 | natural_direction: true 19 | 20 | out_features: 21 | effectiveness: 'high' 22 | natural_direction: true 23 | 24 | 25 | MaxPool2d: 26 | kernel_size: 27 | effectiveness: 'low' 28 | natural_direction: true 29 | 30 | stride: 31 | effectiveness: 'low' 32 | natural_direction: false 33 | 34 | 35 | BatchNorm2d: 36 | num_features: 37 | effectiveness: 'high' 38 | natural_direction: true 39 | -------------------------------------------------------------------------------- /samples/resnet/entry_point.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | import resnet 5 | 6 | 7 | def skyline_model_provider(): 8 | return resnet.resnet50().cuda() 9 | 10 | 11 | def skyline_input_provider(batch_size=16): 12 | return ( 13 | torch.randn((batch_size, 3, 224, 224)).cuda(), 14 | torch.randint(low=0, high=1000, size=(batch_size,)).cuda(), 15 | ) 16 | 17 | 18 | def skyline_iteration_provider(model): 19 | optimizer = torch.optim.SGD(model.parameters(), lr=1e-3) 20 | loss_fn = torch.nn.CrossEntropyLoss() 21 | def iteration(inputs, targets): 22 | optimizer.zero_grad() 23 | out = model(inputs) 24 | loss = loss_fn(out, targets) 25 | loss.backward() 26 | optimizer.step() 27 | return iteration 28 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "website", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "docusaurus start", 7 | "build": "docusaurus build", 8 | "swizzle": "docusaurus swizzle", 9 | "deploy": "docusaurus deploy" 10 | }, 11 | "dependencies": { 12 | "@docusaurus/core": "^2.0.0-alpha.58", 13 | "@docusaurus/preset-classic": "^2.0.0-alpha.58", 14 | "clsx": "^1.1.1", 15 | "react": "^16.8.4", 16 | "react-dom": "^16.8.4" 17 | }, 18 | "browserslist": { 19 | "production": [ 20 | ">0.2%", 21 | "not dead", 22 | "not op_mini all" 23 | ], 24 | "development": [ 25 | "last 1 chrome version", 26 | "last 1 firefox version", 27 | "last 1 safari version" 28 | ] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /plugin/lib/models/Throughput.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | export default class Throughput { 4 | constructor(samplesPerSecond, predictedMaxSamplesPerSecond) { 5 | this._samplesPerSecond = samplesPerSecond; 6 | this._predictedMaxSamplesPerSecond = predictedMaxSamplesPerSecond; 7 | } 8 | 9 | get hasMaxThroughputPrediction() { 10 | return !isNaN(this._predictedMaxSamplesPerSecond); 11 | } 12 | 13 | get samplesPerSecond() { 14 | return this._samplesPerSecond; 15 | } 16 | 17 | get predictedMaxSamplesPerSecond() { 18 | return this._predictedMaxSamplesPerSecond; 19 | } 20 | 21 | static fromThroughputResponse(throughputResponse) { 22 | return new Throughput( 23 | throughputResponse.getSamplesPerSecond(), 24 | throughputResponse.getPredictedMaxSamplesPerSecond(), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /cli/skyline/error_printing.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | def print_analysis_error(error, file=sys.stderr): 5 | print( 6 | "Skyline encountered an error when profiling your model:", 7 | file=file, 8 | ) 9 | print("->", str(error), file=file) 10 | 11 | if error.file_context is not None: 12 | if error.file_context.line_number is not None: 13 | message = ( 14 | "This error occurred on line {} when processing {}.".format( 15 | error.file_context.line_number, 16 | error.file_context.file_path, 17 | ) 18 | ) 19 | else: 20 | message = "This error occurred when processing {}.".format( 21 | error.file_context.file_path, 22 | ) 23 | print("->", message, file=file) 24 | -------------------------------------------------------------------------------- /samples/testnet/entry_point.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | import testnet1 5 | 6 | 7 | class TestNetWithLoss(nn.Module): 8 | def __init__(self): 9 | super().__init__() 10 | self.testnet = testnet1.TestNet() 11 | 12 | def forward(self, input): 13 | return self.testnet(input).sum() 14 | 15 | 16 | def skyline_model_provider(): 17 | return TestNetWithLoss().cuda() 18 | 19 | 20 | def skyline_input_provider(batch_size=32): 21 | return (torch.randn((batch_size, 3, 128, 128)).cuda(),) 22 | 23 | 24 | def skyline_iteration_provider(model): 25 | optimizer = torch.optim.SGD(model.parameters(), lr=1e-3) 26 | def iteration(*inputs): 27 | optimizer.zero_grad() 28 | out = model(*inputs) 29 | out.backward() 30 | optimizer.step() 31 | return iteration 32 | -------------------------------------------------------------------------------- /cli/dev-setup.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | if [ -z "$(which python3)" ]; then 4 | echo "ERROR: Python 3.6+ (python3) must be installed." 5 | exit 1 6 | fi 7 | 8 | if [ -z "$(which pip3)" ]; then 9 | echo "ERROR: Pip for Python 3 (pip3) must be installed." 10 | exit 1 11 | fi 12 | 13 | if [ -z "$(which virtualenv)" ]; then 14 | echo "ERROR: Virtualenv must be installed." 15 | exit 1 16 | fi 17 | 18 | set -e 19 | SCRIPT_PATH=$(cd $(dirname $0) && pwd -P) 20 | cd $SCRIPT_PATH 21 | 22 | virtualenv -p $(which python3) env 23 | source env/bin/activate 24 | pip3 install \ 25 | --global-option="--install-skyline-evaluate" \ 26 | --editable . 27 | 28 | echo "" 29 | echo "Done!" 30 | echo "A development version of Skyline was installed in the 'env' virtualenv." 31 | echo "Activate the virtualenv by running 'source env/bin/activate' inside the 'cli' directory." 32 | -------------------------------------------------------------------------------- /plugin/lib/models/PerfVisState.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | export default { 4 | // When the plugin is waiting for results from the profiler 5 | ANALYZING: 'analyzing', 6 | 7 | // When the plugin is ready for interactions 8 | READY: 'ready', 9 | 10 | // When there are no modified files but Skyline could be showing stale performance data 11 | // NOTE: This state only occurs if the user disables automatic profiling. 12 | READY_STALE: 'ready_stale', 13 | 14 | // When the plugin is showing the user's predictions 15 | SHOWING_PREDICTIONS: 'showing_predictions', 16 | 17 | // When there has been an error processing the user's input 18 | ERROR: 'error', 19 | 20 | // When the user has double clicked the breakdown and is exploring parts of the tree 21 | EXPLORING_WEIGHTS: 'exploring_weights', 22 | EXPLORING_OPERATIONS: 'exploring_operations', 23 | }; 24 | -------------------------------------------------------------------------------- /samples/transformer/transformer/Modules.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import numpy as np 4 | 5 | __author__ = "Yu-Hsiang Huang" 6 | 7 | class ScaledDotProductAttention(nn.Module): 8 | ''' Scaled Dot-Product Attention ''' 9 | 10 | def __init__(self, temperature, attn_dropout=0.1): 11 | super().__init__() 12 | self.temperature = temperature 13 | self.dropout = nn.Dropout(attn_dropout) 14 | self.softmax = nn.Softmax(dim=2) 15 | 16 | def forward(self, q, k, v, mask=None): 17 | 18 | attn = torch.bmm(q, k.transpose(1, 2)) 19 | attn = attn / self.temperature 20 | 21 | if mask is not None: 22 | attn = attn.masked_fill(mask, -np.inf) 23 | 24 | attn = self.softmax(attn) 25 | attn = self.dropout(attn) 26 | output = torch.bmm(attn, v) 27 | 28 | return output, attn 29 | -------------------------------------------------------------------------------- /plugin/styles/PerfVisStatusBar.less: -------------------------------------------------------------------------------- 1 | @import "ui-variables"; 2 | 3 | .innpv-statusbar { 4 | display: flex; 5 | justify-content: space-between; 6 | height: 2.5em; 7 | border-top: 1px solid @inset-panel-border-color; 8 | } 9 | 10 | .innpv-statusbar-message { 11 | margin-left: 5px; 12 | line-height: 25px; 13 | color: @text-color-subtle; 14 | } 15 | 16 | .innpv-statusbar-loading { 17 | margin-right: 5px; 18 | line-height: 2em; 19 | } 20 | 21 | .innpv-statusbar-iconbar { 22 | display: flex; 23 | } 24 | 25 | .innpv-statusbar-icon { 26 | display: flex; 27 | align-items: center; 28 | color: @text-color-subtle; 29 | 30 | .icon::before { 31 | font-size: 1em; 32 | } 33 | } 34 | 35 | .innpv-clickable { 36 | cursor: pointer; 37 | transition-property: color; 38 | transition-duration: 0.2s; 39 | 40 | &:hover { 41 | color: @text-color-highlight; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /cli/skyline/io/sentinel.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | class Sentinel: 5 | def __init__(self): 6 | self._read_pipe = None 7 | self._write_pipe = None 8 | 9 | def start(self): 10 | self._read_pipe, self._write_pipe = os.pipe() 11 | 12 | def stop(self): 13 | os.close(self._write_pipe) 14 | os.close(self._read_pipe) 15 | self._read_pipe = None 16 | self._write_pipe = None 17 | 18 | @property 19 | def read_pipe(self): 20 | return self._read_pipe 21 | 22 | def should_exit(self, ready_descriptors): 23 | return self._read_pipe in ready_descriptors 24 | 25 | def signal_exit(self): 26 | os.write(self._write_pipe, b'\0') 27 | 28 | def consume_exit_signal(self): 29 | # This should only be called after signal_exit(), 30 | # otherwise the calling thread will block. 31 | os.read(self._read_pipe, 1) 32 | -------------------------------------------------------------------------------- /plugin/lib/config/schema.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | // Configuration schemas need to follow the specifications in the 4 | // Atom Config documentation. A default value is **required** 5 | // because we use it to populate our initial configuration state. 6 | // 7 | // https://flight-manual.atom.io/api/v1.43.0/Config/ 8 | 9 | export default { 10 | enableTelemetry: { 11 | title: 'Enable Usage Statistics Reporting', 12 | description: 'Allow usage statistics to be sent to the Skyline team to help improve Skyline.', 13 | type: 'boolean', 14 | default: true, 15 | }, 16 | 17 | disableProfileOnSave: { 18 | title: 'Disable Profile on Save', 19 | description: 'Prevent Skyline from automatically profiling your model each time you save your code. Instead, you will be able to trigger profiling manually by clicking a button in Skyline.', 20 | type: 'boolean', 21 | default: false, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /cli/skyline/tracking/backward_interceptor.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import torch 3 | 4 | from skyline.exceptions import _SuspendExecution 5 | from skyline.tracking.hook_manager import HookManager 6 | 7 | 8 | class BackwardInterceptor: 9 | def __init__(self): 10 | self._backward_hooks = HookManager() 11 | self.backward_root = None 12 | 13 | @contextlib.contextmanager 14 | def intercept(self): 15 | self._backward_hooks.attach_hook( 16 | torch.Tensor, 17 | 'backward', 18 | self._hook_creator, 19 | ) 20 | try: 21 | yield 22 | except _SuspendExecution: 23 | pass 24 | finally: 25 | self._backward_hooks.remove_hooks() 26 | 27 | def _hook_creator(self, fn): 28 | def hook(*args): 29 | self.backward_root = args[0] 30 | raise _SuspendExecution 31 | return hook 32 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # Skyline Website 2 | 3 | The Skyline website (which includes its documentation) is built using 4 | [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. 5 | 6 | To work on the website, you need `node` installed on your machine. According to 7 | the Docusaurus docs, you need version 10.15.1 or greater. Run `node -v` to 8 | check which version of node you have installed. 9 | 10 | 11 | ### Installation 12 | 13 | ```sh 14 | npm install 15 | ``` 16 | 17 | 18 | ### Local Development 19 | 20 | ```sh 21 | npm start 22 | ``` 23 | 24 | This command starts a local development server and open up a browser window. 25 | Most changes are reflected live without having to restart the server. 26 | 27 | 28 | ### Build 29 | 30 | ```sh 31 | npm run build 32 | ``` 33 | 34 | This command generates static content into the `build` directory and can be 35 | served using any static contents hosting service. 36 | -------------------------------------------------------------------------------- /plugin/lib/models/Memory.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | export default class Memory { 4 | constructor(usageMb, maxCapacityMb) { 5 | this._usageMb = usageMb; 6 | this._maxCapacityMb = maxCapacityMb; 7 | } 8 | 9 | get usageMb() { 10 | return this._usageMb; 11 | } 12 | 13 | get maxCapacityMb() { 14 | return this._maxCapacityMb; 15 | } 16 | 17 | get displayPct() { 18 | return this._usageMb / this._maxCapacityMb * 100; 19 | } 20 | 21 | static fromInfo(infoProtobuf) { 22 | return new Memory( 23 | infoProtobuf.getUsageMb(), 24 | infoProtobuf.getMaxCapacityMb(), 25 | ); 26 | } 27 | 28 | static fromPrediction(infoProtobuf, batchSize) { 29 | const usageModel = infoProtobuf.getUsageModelMb(); 30 | const predUsageMb = usageModel.getCoefficient() * batchSize + usageModel.getBias(); 31 | return new Memory( 32 | predUsageMb, 33 | infoProtobuf.getMaxCapacityMb(), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /experimental/components/XDevice.less: -------------------------------------------------------------------------------- 1 | .innpv-xdevice { 2 | flex-grow: 1; 3 | 4 | .innpv-subpanel-content { 5 | flex-direction: column; 6 | justify-content: center; 7 | align-items: center; 8 | } 9 | } 10 | 11 | .innpv-xdevice-bars { 12 | border-left: 2px solid @text-color-subtle; 13 | padding: 5px 0; 14 | } 15 | 16 | .innpv-xdevice-bar { 17 | display: flex; 18 | justify-content: space-between; 19 | height: 20px; 20 | padding: 0 5px; 21 | line-height: 20px; 22 | color: #fff; 23 | 24 | .innpv-xdevice-device { 25 | font-weight: bold; 26 | } 27 | } 28 | 29 | .innpv-xdevice-bar-color-1 { 30 | background-color: #31659e; 31 | } 32 | 33 | .innpv-xdevice-bar-color-2 { 34 | background-color: #4a78aa; 35 | } 36 | 37 | .innpv-xdevice-bar-color-3 { 38 | background-color: #5482be; 39 | } 40 | 41 | .innpv-xdevice-bar-color-4 { 42 | background-color: #74a0de; 43 | } 44 | 45 | .innpv-xdevice-bar-color-5 { 46 | background-color: #95bfff; 47 | } 48 | -------------------------------------------------------------------------------- /plugin/README.md: -------------------------------------------------------------------------------- 1 | Skyline Atom Plugin 2 | =================== 3 | This directory contains code that is specific to the Skyline Atom plugin. A 4 | subset of these files will be copied into the plugin release repository each 5 | time a plugin release is made. 6 | 7 | Development Environment 8 | ----------------------- 9 | To work on the plugin, you need: 10 | 11 | - Atom and `apm` 12 | - Node and `npm` 13 | 14 | Before continuing, make sure you uninstall the Skyline plugin if you have 15 | previously installed it through `apm` or Atom. Then: 16 | 17 | 1. Run `atom-dev-setup.sh` in this directory. 18 | 2. Run `npm ci` in this directory to install the plugin's dependencies. 19 | 20 | The `atom-dev-setup.sh` script will create a symbolic link to the plugin code 21 | so that you can test your changes in Atom during development without having to 22 | reinstall the plugin on each change. If you want to uninstall the development 23 | version of the plugin, run `./atom-dev-setup.sh --uninstall`. 24 | -------------------------------------------------------------------------------- /plugin/lib/redux/views/connection_state.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import ConnectionActions from '../actions/connection'; 4 | 5 | export default class ConnectionStateView { 6 | constructor(store) { 7 | this._store = store; 8 | } 9 | 10 | get _connectionState() { 11 | return this._store.getState().connection; 12 | } 13 | 14 | isInitialized() { 15 | return this._connectionState.initialized; 16 | } 17 | 18 | nextSequenceNumber() { 19 | const nextSequenceNumber = this._connectionState.sequenceNumber; 20 | this._store.dispatch(ConnectionActions.incrementSequence()); 21 | return nextSequenceNumber; 22 | } 23 | 24 | isResponseCurrent(responseSequenceNumber) { 25 | // Since we always increase the sequence number by one, a "current" 26 | // response is one with a sequence number exactly one less than the next 27 | // sequence number to be assigned (this._sequenceNumber). 28 | return responseSequenceNumber === this._connectionState.sequenceNumber - 1; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /website/static/img/skyline.svg: -------------------------------------------------------------------------------- 1 | 2 | image/svg+xml 3 | -------------------------------------------------------------------------------- /plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "skyline", 3 | "main": "./lib/skyline", 4 | "version": "0.5.0", 5 | "description": "Interactive in-editor performance profiling, visualization, and debugging for PyTorch neural networks.", 6 | "keywords": [ 7 | "neural networks", 8 | "pytorch", 9 | "interactive", 10 | "performance", 11 | "visualization", 12 | "profiler", 13 | "debugger" 14 | ], 15 | "activationCommands": { 16 | "atom-workspace": [ 17 | "skyline:toggle" 18 | ] 19 | }, 20 | "uriHandler": { 21 | "method": "handleURI" 22 | }, 23 | "repository": "https://github.com/skylineprof/skyline-atom", 24 | "license": " Apache-2.0", 25 | "engines": { 26 | "atom": ">=1.0.0 <2.0.0" 27 | }, 28 | "dependencies": { 29 | "google-protobuf": "^3.7.0-rc.2", 30 | "react": "^16.8.3", 31 | "react-dom": "^16.8.3", 32 | "react-redux": "^7.1.3", 33 | "redux": "^4.0.5", 34 | "redux-thunk": "^2.3.0", 35 | "universal-analytics": "^0.4.20", 36 | "uuid": "^3.4.0" 37 | }, 38 | "devDependencies": {} 39 | } 40 | -------------------------------------------------------------------------------- /plugin/styles/PerfHint.less: -------------------------------------------------------------------------------- 1 | @import "ui-variables"; 2 | 3 | .innpv-perfhint { 4 | background-color: @input-border-color; 5 | font-family: @font-family; 6 | color: @text-color; 7 | border-radius: 5px; 8 | font-size: 1em; 9 | line-height: 1.5em; 10 | padding: 0 5px; 11 | 12 | .icon::before { 13 | margin-right: 0; 14 | } 15 | 16 | } 17 | 18 | .innpv-perfhint-tooltip-bottom { 19 | transform: translate(-45%, 5px); 20 | animation: fadein 0.5s; 21 | 22 | @keyframes fadein { 23 | from { 24 | opacity: 0; 25 | transform: translate(-45%, 1px); 26 | } 27 | to { 28 | opacity: 1; 29 | transform: translate(-45%, 5px); 30 | } 31 | } 32 | 33 | // Adds a triangle to make a tooltip 34 | &::before { 35 | content: ''; 36 | width: 0; 37 | height: 0; 38 | position: absolute; 39 | left: 50%; 40 | transform: translateX(-50%); 41 | top: -8px; 42 | border-left: 5px solid transparent; 43 | border-right: 5px solid transparent; 44 | border-bottom: 10px solid @input-border-color; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use Skyline, please cite it as below." 3 | authors: 4 | - family-names: "Yu" 5 | given-names: "Geoffrey X." 6 | - family-names: "Grossman" 7 | given-names: "Tovi" 8 | - family-names: "Pekhimenko" 9 | given-names: "Gennady" 10 | title: "Skyline: Interactive In-Editor Computational Performance Profiling for Deep Neural Network Training" 11 | version: 0.5.0 12 | date-released: 2020-07-22 13 | url: "https://github.com/skylineprof/skyline" 14 | preferred-citation: 15 | type: conference-paper 16 | authors: 17 | - family-names: "Yu" 18 | given-names: "Geoffrey X." 19 | - family-names: "Grossman" 20 | given-names: "Tovi" 21 | - family-names: "Pekhimenko" 22 | given-names: "Gennady" 23 | collection-title: "Proceedings of the 33rd Annual ACM Symposium on User Interface Software and Technology (UIST '20)" 24 | doi: 10.1145/3379337.3415890 25 | start: 126 26 | end: 139 27 | title: "Skyline: Interactive In-Editor Computational Performance Profiling for Deep Neural Network Training" 28 | month: 10 29 | year: 2020 30 | -------------------------------------------------------------------------------- /plugin/lib/components/NumericDisplay.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import React from 'react'; 4 | 5 | function isNumeric(candidate) { 6 | return !isNaN(parseFloat(candidate)); 7 | } 8 | 9 | class NumericDisplay extends React.Component { 10 | _numberToDisplay() { 11 | const {number, precision} = this.props; 12 | if (!isNumeric(number)) { 13 | return number; 14 | } 15 | 16 | const fixed = number.toFixed(precision); 17 | const zero = (0).toFixed(precision); 18 | if (number > 0 && fixed === zero) { 19 | return '< 1'; 20 | } 21 | 22 | return fixed; 23 | } 24 | 25 | render() { 26 | const {top, bottom} = this.props; 27 | return ( 28 |
29 |
{top}
30 |
31 | {this._numberToDisplay()} 32 |
33 |
{bottom}
34 |
35 | ); 36 | } 37 | } 38 | 39 | NumericDisplay.defaultProps = { 40 | precision: 0, 41 | }; 42 | 43 | export default NumericDisplay; 44 | -------------------------------------------------------------------------------- /plugin/lib/logger_impl.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | const LogLevel = { 4 | TRACE: 0, 5 | DEBUG: 1, 6 | INFO: 2, 7 | WARN: 3, 8 | ERROR: 4, 9 | }; 10 | 11 | class Logger { 12 | constructor(logLevel) { 13 | this._logLevel = logLevel; 14 | } 15 | 16 | error(...args) { 17 | if (this._logLevel > LogLevel.ERROR) { 18 | return; 19 | } 20 | this._log(console.error, ...args); 21 | } 22 | 23 | warn(...args) { 24 | if (this._logLevel > LogLevel.WARN) { 25 | return; 26 | } 27 | this._log(console.warn, ...args); 28 | } 29 | 30 | info(...args) { 31 | if (this._logLevel > LogLevel.INFO) { 32 | return; 33 | } 34 | this._log(console.info, ...args); 35 | } 36 | 37 | debug(...args) { 38 | if (this._logLevel > LogLevel.DEBUG) { 39 | return; 40 | } 41 | this._log(console.debug, ...args); 42 | } 43 | 44 | trace(...args) { 45 | if (this._logLevel > LogLevel.TRACE) { 46 | return; 47 | } 48 | this._log(console.trace, ...args); 49 | } 50 | 51 | _log(loggingFn, ...args) { 52 | loggingFn('[Skyline]', ...args); 53 | } 54 | } 55 | 56 | export {Logger, LogLevel}; 57 | -------------------------------------------------------------------------------- /samples/gnmt/seq2seq/train/smoothing.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | 4 | 5 | class LabelSmoothing(nn.Module): 6 | """ 7 | NLL loss with label smoothing. 8 | """ 9 | def __init__(self, padding_idx, smoothing=0.0): 10 | """ 11 | Constructor for the LabelSmoothing module. 12 | 13 | :param padding_idx: index of the PAD token 14 | :param smoothing: label smoothing factor 15 | """ 16 | super(LabelSmoothing, self).__init__() 17 | self.padding_idx = padding_idx 18 | self.confidence = 1.0 - smoothing 19 | self.smoothing = smoothing 20 | 21 | def forward(self, x, target): 22 | logprobs = torch.nn.functional.log_softmax(x, dim=-1, 23 | dtype=torch.float32) 24 | 25 | non_pad_mask = (target != self.padding_idx) 26 | nll_loss = -logprobs.gather(dim=-1, index=target.unsqueeze(1)) 27 | nll_loss = nll_loss.squeeze(1)[non_pad_mask] 28 | smooth_loss = -logprobs.mean(dim=-1)[non_pad_mask] 29 | loss = self.confidence * nll_loss + self.smoothing * smooth_loss 30 | return loss.sum() 31 | -------------------------------------------------------------------------------- /samples/transformer/transformer/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Victor Huang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | ======================================== 2 | Copyright Notice for the Skyline Project 3 | ======================================== 4 | 5 | Copyright 2020 Geoffrey X. Yu 6 | Copyright 2020 Tovi Grossman 7 | Copyright 2020 Gennady Pekhimenko 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); 10 | you may not use this project except in compliance with the License. 11 | You may obtain a copy of the License at 12 | 13 | http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | Unless required by applicable law or agreed to in writing, software 16 | distributed under the License is distributed on an "AS IS" BASIS, 17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | See the License for the specific language governing permissions and 19 | limitations under the License. 20 | 21 | 22 | ===================================== 23 | Copyright Notice for the Code Samples 24 | ===================================== 25 | 26 | Portions of code inside the "samples" directory were written by 27 | third party developers. These code files carry their own open 28 | source licenses and copyright notices. Please see the README.md 29 | and LICENSE files inside those directories for more information. 30 | -------------------------------------------------------------------------------- /samples/gnmt/seq2seq/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Elad Hoffer 4 | Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /cli/skyline/models/source_map.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class Position: 4 | def __init__(self, line, column): 5 | self.line = line 6 | self.column = column 7 | 8 | def offset(self, length): 9 | return Position(self.line, self.column + length) 10 | 11 | 12 | class SourceMap: 13 | def __init__(self, source_code): 14 | self._source_by_line = source_code.splitlines() 15 | 16 | def find_position(self, snippet, line_offset=0): 17 | for offset, line in enumerate(self._source_by_line[line_offset:]): 18 | index = line.find(snippet) 19 | if index == -1: 20 | continue 21 | # NOTE: We don't add 1 here to make the line number 0-based 22 | return Position(line_offset + offset, index) 23 | 24 | return None 25 | 26 | def find_position_on_line(self, snippet, offset_position): 27 | if offset_position.line >= len(self._source_by_line): 28 | return None 29 | index = self._source_by_line[offset_position.line].find( 30 | snippet, offset_position.column) 31 | 32 | if index == -1: 33 | return None 34 | else: 35 | return Position(offset_position.line, index) 36 | -------------------------------------------------------------------------------- /cli/skyline/__main__.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import enum 3 | import sys 4 | 5 | import skyline 6 | import skyline.commands.interactive 7 | import skyline.commands.memory 8 | import skyline.commands.time 9 | 10 | 11 | def main(): 12 | parser = argparse.ArgumentParser( 13 | prog="skyline", 14 | description="Skyline: Interactive Neural Network Performance " 15 | "Profiler, Visualizer, and Debugger for PyTorch", 16 | ) 17 | parser.add_argument( 18 | "-v", "--version", 19 | action="store_true", 20 | help="Print the version and exit.", 21 | ) 22 | subparsers = parser.add_subparsers(title="Commands") 23 | skyline.commands.interactive.register_command(subparsers) 24 | skyline.commands.memory.register_command(subparsers) 25 | skyline.commands.time.register_command(subparsers) 26 | args = parser.parse_args() 27 | 28 | if args.version: 29 | print('Skyline Command Line Interface', 'v' + skyline.__version__) 30 | return 31 | 32 | if 'func' not in args: 33 | parser.print_help() 34 | sys.exit(1) 35 | 36 | # Run the specified command 37 | args.func(args) 38 | 39 | 40 | if __name__ == '__main__': 41 | main() 42 | -------------------------------------------------------------------------------- /samples/legacy/lenet.py: -------------------------------------------------------------------------------- 1 | import torch.nn as nn 2 | 3 | 4 | class LeNet(nn.Module): 5 | def __init__(self): 6 | super(LeNet, self).__init__() 7 | self.conv1 = nn.Conv2d(in_channels=3, out_channels=20, kernel_size=5) 8 | self.conv2 = nn.Conv2d(in_channels=20, out_channels=50, kernel_size=5) 9 | self.dense1 = nn.Linear(in_features=1250, out_features=500) 10 | self.dense2 = nn.Linear(in_features=500, out_features=10) 11 | self.tanh = nn.Tanh() 12 | self.pool = nn.MaxPool2d(kernel_size=2, stride=2) 13 | self.softmax = nn.LogSoftmax(dim=1) 14 | 15 | def forward(self, input): 16 | """ 17 | LeNet for CIFAR-10 18 | @innpv size (512, 3, 32, 32) 19 | """ 20 | output = self.conv1(input) 21 | output = self.tanh(output) 22 | output = self.pool(output) 23 | 24 | output = self.conv2(output) 25 | output = self.tanh(output) 26 | output = self.pool(output) 27 | 28 | output = output.view(-1, 1250) 29 | 30 | output = self.dense1(output) 31 | output = self.tanh(output) 32 | output = self.dense2(output) 33 | output = self.softmax(output) 34 | 35 | return output 36 | -------------------------------------------------------------------------------- /samples/transformer/transformer/Optim.py: -------------------------------------------------------------------------------- 1 | '''A wrapper class for optimizer ''' 2 | import numpy as np 3 | 4 | class ScheduledOptim(): 5 | '''A simple wrapper class for learning rate scheduling''' 6 | 7 | def __init__(self, optimizer, d_model, n_warmup_steps): 8 | self._optimizer = optimizer 9 | self.n_warmup_steps = n_warmup_steps 10 | self.n_current_steps = 0 11 | self.init_lr = np.power(d_model, -0.5) 12 | 13 | def step_and_update_lr(self): 14 | "Step with the inner optimizer" 15 | self._update_learning_rate() 16 | self._optimizer.step() 17 | 18 | def zero_grad(self): 19 | "Zero out the gradients by the inner optimizer" 20 | self._optimizer.zero_grad() 21 | 22 | def _get_lr_scale(self): 23 | return np.min([ 24 | np.power(self.n_current_steps, -0.5), 25 | np.power(self.n_warmup_steps, -1.5) * self.n_current_steps]) 26 | 27 | def _update_learning_rate(self): 28 | ''' Learning rate scheduling per step ''' 29 | 30 | self.n_current_steps += 1 31 | lr = self.init_lr * self._get_lr_scale() 32 | 33 | for param_group in self._optimizer.param_groups: 34 | param_group['lr'] = lr 35 | 36 | -------------------------------------------------------------------------------- /cli/skyline/config/__init__.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | 3 | import skyline.data 4 | 5 | 6 | class _Config: 7 | def __init__(self): 8 | self.Hints = None 9 | 10 | self.warm_up = 100 11 | self.measure_for = 10 12 | 13 | self.project_root = None 14 | self.entry_point = None 15 | 16 | def initialize_hints_config(self, hints_file): 17 | if hints_file is None: 18 | file_to_open = skyline.data.get_absolute_path('hints.yml') 19 | else: 20 | file_to_open = hints_file 21 | 22 | with open(file_to_open, 'r') as f: 23 | self.Hints = yaml.load(f, Loader=yaml.Loader) 24 | 25 | def parse_args(self, args): 26 | if 'hints_file' not in args: 27 | args.hints_file = None 28 | self.initialize_hints_config(args.hints_file) 29 | 30 | if 'warm_up' in args and args.warm_up is not None: 31 | self.warm_up = args.warm_up 32 | if 'measure_for' in args and args.measure_for is not None: 33 | self.measure_for = args.measure_for 34 | 35 | def set_project_paths(self, project_root, entry_point): 36 | self.project_root = project_root 37 | self.entry_point = entry_point 38 | 39 | 40 | Config = _Config() 41 | -------------------------------------------------------------------------------- /plugin/lib/components/ErrorMessage.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import React from 'react'; 4 | 5 | import PerfVisState from '../models/PerfVisState'; 6 | 7 | export default class ErrorMessage extends React.Component { 8 | _classes() { 9 | const mainClass = 'innpv-error'; 10 | const {perfVisState, projectModified} = this.props; 11 | if (projectModified || perfVisState === PerfVisState.ANALYZING) { 12 | return mainClass + ' innpv-no-events'; 13 | } 14 | return mainClass; 15 | } 16 | 17 | render() { 18 | return ( 19 |
20 |
21 |

Analysis Error

22 |

{this.props.message}

23 | {this._renderFileContext()} 24 |
25 |
26 | ); 27 | } 28 | 29 | _renderFileContext() { 30 | const {filePath, lineNumber} = this.props; 31 | if (filePath == null) { 32 | return null; 33 | } 34 | 35 | let text = null; 36 | if (lineNumber == null) { 37 | text = `This error occurred when processing ${filePath}.`; 38 | } else { 39 | text = `This error occurred on line ${lineNumber} when processing ${filePath}.`; 40 | } 41 | return

{text}

; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /plugin/lib/io/message_sender.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import pm from '../protocol_gen/innpv_pb'; 4 | import Events from '../telemetry/events'; 5 | 6 | export default class MessageSender { 7 | constructor({connection, connectionStateView, telemetryClient}) { 8 | this._connection = connection; 9 | this._connectionStateView = connectionStateView; 10 | this._telemetryClient = telemetryClient; 11 | } 12 | 13 | sendInitializeRequest() { 14 | const message = new pm.InitializeRequest(); 15 | // Version 1 - v0.1.x 16 | // Version 2 - v0.2.x 17 | // Version 3 - v0.3.x 18 | // Version 4 - v0.4.x 19 | message.setProtocolVersion(5); 20 | this._sendMessage(message, 'Initialize'); 21 | } 22 | 23 | sendAnalysisRequest() { 24 | const message = new pm.AnalysisRequest(); 25 | this._sendMessage(message, 'Analysis'); 26 | this._telemetryClient.record(Events.Skyline.REQUESTED_ANALYSIS); 27 | } 28 | 29 | _sendMessage(message, payloadName) { 30 | const enclosingMessage = new pm.FromClient(); 31 | enclosingMessage['set' + payloadName](message); 32 | enclosingMessage.setSequenceNumber( 33 | this._connectionStateView.nextSequenceNumber(), 34 | ); 35 | this._connection.sendBytes(enclosingMessage.serializeBinary()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cli/skyline/tracking/base.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import sqlite3 3 | 4 | 5 | class TrackerBase: 6 | def __init__(self): 7 | self._is_tracking = False 8 | 9 | @contextlib.contextmanager 10 | def track(self): 11 | self.start_tracking() 12 | try: 13 | yield self 14 | finally: 15 | self.stop_tracking() 16 | 17 | def start_tracking(self): 18 | self._is_tracking = True 19 | 20 | def stop_tracking(self): 21 | self._is_tracking = False 22 | 23 | def populate_report(self, builder): 24 | raise NotImplementedError 25 | 26 | 27 | class ReportBase: 28 | def __init__(self, connection): 29 | self._connection = connection 30 | 31 | def __del__(self): 32 | self._connection.close() 33 | 34 | 35 | class ReportBuilderBase: 36 | def __init__(self, file=None): 37 | database_file = file if file is not None else ':memory:' 38 | self._connection = sqlite3.connect(database_file) 39 | self._create_report_tables() 40 | 41 | def process_tracker(self, tracker): 42 | tracker.populate_report(self) 43 | return self 44 | 45 | def build(self): 46 | raise NotImplementedError 47 | 48 | def _create_report_tables(self): 49 | raise NotImplementedError 50 | -------------------------------------------------------------------------------- /website/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | /** 3 | * Any CSS included here will be global. The classic template 4 | * bundles Infima by default. Infima is a CSS framework designed to 5 | * work well for content-centric websites. 6 | */ 7 | 8 | /* You can override the default Infima variables here. */ 9 | :root { 10 | --ifm-color-primary: #0861ad; 11 | --ifm-color-primary-dark: #07579c; 12 | --ifm-color-primary-darker: #075293; 13 | --ifm-color-primary-darkest: #064479; 14 | --ifm-color-primary-light: #096bbe; 15 | --ifm-color-primary-lighter: #0970c7; 16 | --ifm-color-primary-lightest: #0a7ee1; 17 | --ifm-code-font-size: 95%; 18 | } 19 | 20 | .docusaurus-highlight-code-line { 21 | background-color: rgb(72, 77, 91); 22 | display: block; 23 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 24 | padding: 0 var(--ifm-pre-padding); 25 | } 26 | 27 | .navbar__logo { 28 | height: 80%; 29 | margin-right: 1rem; 30 | } 31 | 32 | .modest-link, 33 | .modest-link:hover { 34 | color: var(--ifm-font-color-base); 35 | } 36 | 37 | @media screen and (max-width: 996px) { 38 | .hero--button { 39 | margin: 0 10px 20px 10px; 40 | } 41 | } 42 | 43 | @media screen and (min-width: 997px) { 44 | .hero--button--row a:first-child { 45 | margin-right: 20px; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /website/docs/providers.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: providers 3 | title: Providers in Detail 4 | --- 5 | ### Model Provider 6 | 7 | ```python 8 | def skyline_model_provider() -> torch.nn.Module: 9 | pass 10 | ``` 11 | 12 | The model provider must take no arguments and return an instance of your model 13 | (a `torch.nn.Module`) that is on the GPU (i.e. you need to call `.cuda()` on 14 | the module before returning it). 15 | 16 | 17 | ### Input Provider 18 | 19 | ```python 20 | def skyline_input_provider(batch_size: int = 32) -> Tuple: 21 | pass 22 | ``` 23 | 24 | The input provider must take a single `batch_size` argument that has a default 25 | value (the batch size you want to profile with). It must return an iterable 26 | (does not *have* to be a `tuple`) that contains the arguments that you would 27 | normally pass to your model's `forward` method. Any `Tensor`s in the returned 28 | iterable must be on the GPU (i.e. you need to call `.cuda()` on them before 29 | returning them). 30 | 31 | 32 | ### Iteration Provider 33 | 34 | ```python 35 | def skyline_iteration_provider(model: torch.nn.Module) -> Callable: 36 | pass 37 | ``` 38 | 39 | The iteration provider must take a single `model` argument, which will be an 40 | instance of your model. This provider must return a callable (e.g., a function) 41 | that, when invoked, runs a single training iteration. 42 | -------------------------------------------------------------------------------- /website/src/pages/styles.module.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | 3 | /** 4 | * CSS files with the .module.css suffix will be treated as CSS modules 5 | * and scoped locally. 6 | */ 7 | 8 | .heroBannerRow { 9 | align-items: center; 10 | } 11 | 12 | .heroBannerImage { 13 | height: auto; 14 | border-radius: 5px; 15 | } 16 | 17 | @media screen and (max-width: 966px) { 18 | .heroBanner { 19 | padding: 2rem; 20 | } 21 | } 22 | 23 | @media screen and (max-width: 996px) { 24 | .heroText { 25 | margin-bottom: 20px; 26 | text-align: center; 27 | } 28 | } 29 | 30 | .buttons { 31 | display: flex; 32 | align-items: center; 33 | justify-content: center; 34 | } 35 | 36 | .features { 37 | display: flex; 38 | align-items: center; 39 | padding: 2rem 0; 40 | width: 100%; 41 | } 42 | 43 | .featureImage { 44 | height: 200px; 45 | width: 200px; 46 | } 47 | 48 | .feature { 49 | text-align: justify; 50 | hyphens: auto; 51 | } 52 | 53 | .paperTitle { 54 | margin-top: 40px; 55 | margin-bottom: 30px; 56 | } 57 | 58 | .paperTitle span { 59 | margin-right: 10px; 60 | } 61 | 62 | .authorsList { 63 | margin-bottom: 10px; 64 | } 65 | 66 | .paperAuthor { 67 | margin-bottom: 20px; 68 | } 69 | 70 | .paperAbstract { 71 | text-align: justify; 72 | hyphens: auto; 73 | margin-bottom: 30px; 74 | } 75 | -------------------------------------------------------------------------------- /plugin/lib/components/BarSlider.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import React from 'react'; 4 | 5 | import Resizable from './Resizable'; 6 | 7 | class BarSlider extends React.Component { 8 | render() { 9 | const { 10 | percentage, 11 | limitPercentage, 12 | onMouseEnter, 13 | onMouseLeave, 14 | onClick, 15 | } = this.props; 16 | const limitBarHeight = 100 - limitPercentage; 17 | return ( 18 |
24 |
25 | 30 |
31 | 32 | {limitBarHeight > 1e-3 ? 33 |
: null} 34 |
35 |
36 | ); 37 | } 38 | } 39 | 40 | BarSlider.defaultProps = { 41 | limitPercentage: 100, 42 | handleResize: () => {}, 43 | onClick: () => {}, 44 | onMouseEnter: () => {}, 45 | onMouseLeave: () => {}, 46 | }; 47 | 48 | export default BarSlider; 49 | -------------------------------------------------------------------------------- /plugin/styles/GetStarted.less: -------------------------------------------------------------------------------- 1 | @import "ui-variables"; 2 | 3 | .innpv-get-started { 4 | height: 100%; 5 | width: 100%; 6 | padding: @component-padding; 7 | 8 | display: flex; 9 | justify-content: center; 10 | align-items: center; 11 | flex-direction: column; 12 | } 13 | 14 | .innpv-get-started-text { 15 | width: 100%; 16 | 17 | h1 { 18 | font-size: 3em; 19 | font-weight: bold; 20 | font-style: italic; 21 | text-align: center; 22 | } 23 | 24 | p { 25 | text-align: center; 26 | } 27 | } 28 | 29 | .innpv-get-started-buttons { 30 | text-align: center; 31 | } 32 | 33 | .innpv-get-started-options { 34 | width: 100%; 35 | border-top: 1px solid @base-border-color; 36 | padding-top: 15px; 37 | margin-top: 20px; 38 | } 39 | 40 | .innpv-get-started-host-port-fields { 41 | display: flex; 42 | justify-content: space-between; 43 | 44 | p { 45 | margin: 0; 46 | text-align: left; 47 | } 48 | } 49 | 50 | .innpv-get-started-host { 51 | width: 75%; 52 | } 53 | 54 | .innpv-get-started-port { 55 | width: 20%; 56 | } 57 | 58 | .innpv-get-started-project-root { 59 | margin-top: 10px; 60 | 61 | p { 62 | margin: 0; 63 | } 64 | } 65 | 66 | .innpv-get-started-project-root-row { 67 | display: flex; 68 | justify-content: space-between; 69 | 70 | input { 71 | margin-right: 5px; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /experimental/components/XDevice.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import React from 'react'; 4 | 5 | import Subheader from './Subheader'; 6 | 7 | function XDeviceBar(props) { 8 | return ( 9 |
13 | {props.device} 14 | {props.speedup} 15 |
16 | ); 17 | } 18 | 19 | export default class XDevice extends React.Component { 20 | constructor(props) { 21 | super(props); 22 | } 23 | 24 | render() { 25 | return ( 26 |
27 | Throughput Predictions 28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 |
36 |
37 |
38 | ); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /cli/README.md: -------------------------------------------------------------------------------- 1 | Skyline Command Line Interface (CLI) 2 | ==================================== 3 | This directory contains the code that implements Skyline's command line 4 | interface (CLI). The CLI is written in Python and can be installed as an 5 | executable. 6 | 7 | Right now, the CLI serves as the entrypoint for the interactive profiler. Users 8 | can start a profiling session by running 9 | 10 | ``` 11 | $ skyline interactive 12 | ``` 13 | 14 | Development Environment 15 | ----------------------- 16 | To set up a development version of the CLI, use `pip` to install an editable 17 | version of the `skyline` package. We recommend that you do this inside a 18 | virtual Python environment such as `virtualenv` since this process will install 19 | other Python packages as well (Skyline's dependencies). 20 | 21 | For your convenience, you can run the `dev-setup.sh` script to create a 22 | development environment inside a virtualenv: 23 | 24 | ```sh 25 | # You only need to run this once 26 | ./dev-setup.sh 27 | 28 | # Run this to activate your development environment 29 | source env/bin/activate 30 | 31 | # Test out Skyline 32 | skyline --help 33 | ``` 34 | 35 | If you want to set up your development environment manually: 36 | 37 | ```sh 38 | # To install a development version of the CLI, run (inside this directory): 39 | pip3 install --editable . 40 | ``` 41 | -------------------------------------------------------------------------------- /plugin/lib/config/manager.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import {CompositeDisposable} from 'atom'; 4 | import configSchema from './schema'; 5 | import Logger from '../logger'; 6 | 7 | import ConfigActions from '../redux/actions/config'; 8 | 9 | // The purpose of this class is to subscribe to our configuration schemas 10 | // through Atom's configuration API and to update our store with the latest 11 | // values. This helps reduce the coupling between our components and the 12 | // Atom APIs. 13 | export default class ConfigManager { 14 | constructor(store) { 15 | this._store = store; 16 | this._subscriptions = this._subscribeToConfigs(); 17 | } 18 | 19 | dispose() { 20 | this._subscriptions.dispose(); 21 | this._store = null; 22 | } 23 | 24 | _subscribeToConfigs() { 25 | const subscriptions = new CompositeDisposable(); 26 | for (const key in configSchema) { 27 | if (!Object.prototype.hasOwnProperty.call(configSchema, key)) { 28 | continue; 29 | } 30 | subscriptions.add(atom.config.observe( 31 | `skyline.${key}`, this._handleConfigChange.bind(this, key))); 32 | } 33 | return subscriptions; 34 | } 35 | 36 | _handleConfigChange(key, value) { 37 | if (this._store == null) { 38 | return; 39 | } 40 | this._store.dispatch(ConfigActions.configChanged({ 41 | [key]: value, 42 | })); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /cli/skyline/profiler/__init__.py: -------------------------------------------------------------------------------- 1 | import torch 2 | 3 | from skyline.exceptions import AnalysisError 4 | from skyline.models.analysis import PerformanceLimits 5 | 6 | 7 | def to_trainable_model(parse_tree, class_name): 8 | try: 9 | executable = compile(parse_tree, '', 'exec') 10 | scope = {} 11 | exec(executable, scope, scope) 12 | model = scope[class_name]().to(torch.device('cuda')) 13 | model.train() 14 | return model 15 | except Exception as ex: 16 | raise AnalysisError(str(ex), type(ex)) 17 | 18 | 19 | def get_performance_limits(memory_info, throughput_info): 20 | max_capacity_batch_size = memory_info.usage_model_mb.inverse( 21 | memory_info.max_capacity_mb) 22 | max_capacity_throughput = ( 23 | max_capacity_batch_size / 24 | throughput_info.runtime_model_ms.evaluate(max_capacity_batch_size) * 25 | 1000 26 | ) 27 | max_throughput_batch_size = throughput_info.batch_from_throughput( 28 | throughput_info.max_throughput) 29 | 30 | thpt_limits = (max_throughput_batch_size, throughput_info.max_throughput) 31 | mem_limits = (max_capacity_batch_size, max_capacity_throughput) 32 | 33 | limits = min(thpt_limits, mem_limits, key=lambda tup: tup[0]) 34 | 35 | return PerformanceLimits( 36 | max_batch_size=limits[0], 37 | throughput_limit=limits[1], 38 | ) 39 | -------------------------------------------------------------------------------- /plugin/styles/BarSlider.less: -------------------------------------------------------------------------------- 1 | @import "ui-variables"; 2 | @import "innpv-animation"; 3 | 4 | @bar-color: #449dd1; 5 | 6 | .innpv-barslider { 7 | width: 80px; 8 | margin: 20px 0 20px 10px; 9 | border-bottom: 1px solid @background-color-selected; 10 | 11 | &:hover { 12 | .innpv-barslider-bar { 13 | opacity: 1; 14 | } 15 | } 16 | } 17 | 18 | .innpv-barslider-barwrap { 19 | height: 100%; 20 | width: 35px; 21 | margin: 0 auto; 22 | background-color: @base-background-color; 23 | 24 | display: flex; 25 | align-items: flex-end; 26 | justify-content: space-between; 27 | flex-direction: column-reverse; 28 | } 29 | 30 | .innpv-resizable-barslider { 31 | width: 100%; 32 | transition-property: height; 33 | transition-duration: @perfbar-duration; 34 | transition-timing-function: @perfbar-easing; 35 | } 36 | 37 | .innpv-barslider-bar { 38 | width: 100%; 39 | height: 100%; 40 | background-color: @bar-color; 41 | opacity: 0.6; 42 | cursor: row-resize; 43 | transition-property: opacity; 44 | transition-duration: @perfbar-duration; 45 | transition-timing-function: @perfbar-easing; 46 | } 47 | 48 | .innpv-barslider-limit { 49 | width: 100%; 50 | background: repeating-linear-gradient( 51 | 45deg, 52 | @background-color-selected, 53 | @background-color-selected 8px, 54 | @background-color-highlight 8px, 55 | @background-color-highlight 16px 56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /website/docs/misc.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: misc 3 | title: Miscellaneous 4 | --- 5 | 6 | ## Versioning 7 | 8 | Skyline uses semantic versioning. Before the 1.0.0 release, backward 9 | compatibility between minor versions will not be guaranteed. 10 | 11 | The Skyline command line tool and plugin use *independent* version numbers. 12 | However, it is very likely that minor and major versions of the command line 13 | tool and plugin will be released together (and hence share major/minor version 14 | numbers). 15 | 16 | Generally speaking, the most recent version of the command line tool and plugin 17 | will be compatible with each other. 18 | 19 | 20 | ## License 21 | 22 | Skyline is open source software that is licensed under the Apache 2.0 License. 23 | Please see the `LICENSE` and `NOTICE` files in the Skyline repository for more 24 | information. 25 | 26 | Inside the `samples` directory, we include code samples from third party 27 | developers that carry their own open source licenses. Please see the 28 | `README.md` and `LICENSE` files inside those directories for more information. 29 | 30 | 31 | ## Authors 32 | 33 | Skyline was written by and is primarily maintained by Geoffrey Yu 34 | (gxyu@cs.toronto.edu). 35 | 36 | Skyline began as a research project at the [University of 37 | Toronto](https://web.cs.toronto.edu) in collaboration with [Tovi 38 | Grossman](https://www.tovigrossman.com) and [Gennady 39 | Pekhimenko](https://www.cs.toronto.edu/~pekhimenko/). 40 | -------------------------------------------------------------------------------- /website/docs/settings.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: settings 3 | title: Settings 4 | --- 5 | 6 | This page describes the settings available in the Skyline interactive profiler 7 | (i.e. the Skyline Atom plugin). You can access Skyline's settings through 8 | Atom's preferences pane just like any other Atom package (Atom > 9 | Settings/Preferences > Packages > Skyline > Settings). 10 | 11 | 12 | ### Disable Profile on Save 13 | 14 | By default, Skyline will profile your model whenever you change and then save 15 | your code. You can use this setting to prevent Skyline from doing this 16 | automatic profiling. 17 | 18 | If you do disable profiling on save, Skyline will display a message in its 19 | status bar (bottom right corner of the Atom window) when its performance 20 | visualizations could be out of date. To request re-profiling when this happens, 21 | click the "cycle" button that will appear on the bottom right corner of the 22 | Atom window. 23 | 24 | :::note 25 | Skyline's profiling takes place in the background and does not interfere with 26 | your ability to write code and use Atom. As a result, we recommend *not* 27 | disabling profiling on save. 28 | ::: 29 | 30 | 31 | ### Enable Usage Statistics Reporting 32 | 33 | Skyline will collect anonymous usage statistics to help us understand how 34 | Skyline is being used and to help us make further improvements to Skyline. If 35 | you do not want Skyline to send these usage statistics, you can use this 36 | setting to disable their collection. 37 | -------------------------------------------------------------------------------- /cli/skyline/analysis/runner.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import logging 3 | import os 4 | 5 | from skyline.analysis.session import AnalysisSession 6 | from skyline.nvml import NVML 7 | 8 | 9 | def analyze_project(project_root, entry_point, nvml): 10 | session = AnalysisSession.new_from(project_root, entry_point) 11 | yield session.measure_breakdown(nvml) 12 | yield session.measure_throughput() 13 | 14 | 15 | def main(): 16 | # This is used for development and debugging purposes 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument("entry_point", type=str) 19 | args = parser.parse_args() 20 | 21 | project_root = os.getcwd() 22 | with NVML() as nvml: 23 | analyzer = analyze_project(project_root, args.entry_point, nvml) 24 | breakdown = next(analyzer) 25 | throughput = next(analyzer) 26 | 27 | print('Peak usage: ', breakdown.peak_usage_bytes, 'bytes') 28 | print('Max. capacity:', breakdown.memory_capacity_bytes, 'bytes') 29 | print('No. of weight breakdown nodes: ', len(breakdown.operation_tree)) 30 | print('No. of operation breakdown nodes:', len(breakdown.weight_tree)) 31 | print('Throughput:', throughput.samples_per_second, 'samples/s') 32 | 33 | 34 | if __name__ == "__main__": 35 | kwargs = { 36 | "format": "%(asctime)s %(levelname)-8s %(message)s", 37 | "datefmt": "%Y-%m-%d %H:%M", 38 | "level": logging.DEBUG, 39 | } 40 | logging.basicConfig(**kwargs) 41 | main() 42 | -------------------------------------------------------------------------------- /cli/skyline/tracking/time/report_queries.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | create_report_tables = { 4 | 'run_time_entries': """ 5 | CREATE TABLE IF NOT EXISTS run_time_entries ( 6 | id INTEGER PRIMARY KEY, 7 | operation_name TEXT NOT NULL, 8 | forward_ms REAL NOT NULL, 9 | backward_ms REAL 10 | ) 11 | """, 12 | 'stack_frames': """ 13 | CREATE TABLE IF NOT EXISTS stack_frames ( 14 | ordering INTEGER NOT NULL, 15 | file_path TEXT NOT NULL, 16 | line_number INTEGER NOT NULL, 17 | entry_id INTEGER NOT NULL, 18 | PRIMARY KEY (entry_id, ordering) 19 | ) 20 | """, 21 | } 22 | 23 | set_report_format_version = 'PRAGMA user_version = {version:d}' 24 | 25 | add_stack_frame = """ 26 | INSERT INTO stack_frames (ordering, file_path, line_number, entry_id) 27 | VALUES (?, ?, ?, ?) 28 | """ 29 | 30 | add_run_time_entry = """ 31 | INSERT INTO run_time_entries (operation_name, forward_ms, backward_ms) 32 | VALUES (?, ?, ?) 33 | """ 34 | 35 | get_run_time_entries_with_context = """ 36 | WITH code_contexts AS ( 37 | SELECT entry_id, file_path, line_number FROM stack_frames 38 | GROUP BY entry_id HAVING ordering == MIN(ordering) 39 | ) 40 | SELECT 41 | e.operation_name, 42 | e.forward_ms, 43 | e.backward_ms, 44 | c.file_path, 45 | c.line_number 46 | FROM 47 | run_time_entries AS e LEFT JOIN code_contexts AS c 48 | ON e.id == c.entry_id 49 | ORDER BY c.file_path ASC, c.line_number ASC 50 | """ 51 | -------------------------------------------------------------------------------- /samples/gnmt/seq2seq/data/config.py: -------------------------------------------------------------------------------- 1 | PAD_TOKEN = '' 2 | UNK_TOKEN = '' 3 | BOS_TOKEN = '' 4 | EOS_TOKEN = '<\s>' 5 | 6 | # special PAD, UNKNOWN, BEGIN-OF-STRING, END-OF-STRING tokens 7 | PAD, UNK, BOS, EOS = [0, 1, 2, 3] 8 | 9 | # path to the BPE vocabulary file, relative to the data directory, it should 10 | # point to file generated by subword-nmt/get_vocab.py 11 | VOCAB_FNAME = 'vocab.bpe.32000' 12 | 13 | # paths to source and target training files, relative to the data directory, it 14 | # should point to BPE-encoded files, generated by subword-nmt/apply_bpe.py 15 | SRC_TRAIN_FNAME = 'train.tok.clean.bpe.32000.en' 16 | TGT_TRAIN_FNAME = 'train.tok.clean.bpe.32000.de' 17 | 18 | # paths to source and target validation files, relative to the data directory, 19 | # it should point to BPE-encoded files, generated by subword-nmt/apply_bpe.py 20 | SRC_VAL_FNAME = 'newstest_dev.tok.clean.bpe.32000.en' 21 | TGT_VAL_FNAME = 'newstest_dev.tok.clean.bpe.32000.de' 22 | 23 | # path to the test source file, relative to the data directory, it should point 24 | # to BPE-encoded file, generated by subword-nmt/apply_bpe.py 25 | SRC_TEST_FNAME = 'newstest2014.tok.bpe.32000.en' 26 | 27 | # path to the test target file, relative to the data directory, it should point 28 | # to plaintext file, tokenization is performed by the sacrebleu package 29 | TGT_TEST_TARGET_FNAME = 'newstest2014.de' 30 | 31 | # path to the moses detokenizer, relative to the data directory 32 | DETOKENIZER = 'mosesdecoder/scripts/tokenizer/detokenizer.perl' 33 | -------------------------------------------------------------------------------- /plugin/lib/skyline.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import {CompositeDisposable, Disposable} from 'atom'; 4 | import SkylinePlugin from './skyline_plugin'; 5 | import configSchema from './config/schema'; 6 | 7 | export default { 8 | _plugin: null, 9 | _subscriptions: null, 10 | 11 | activate() { 12 | this._subscriptions = new CompositeDisposable( 13 | atom.commands.add('atom-workspace', { 14 | 'skyline:toggle': () => this.toggle(), 15 | }), 16 | new Disposable(this._disposePlugin.bind(this)), 17 | ); 18 | }, 19 | 20 | deactivate() { 21 | this._subscriptions.dispose(); 22 | }, 23 | 24 | config: configSchema, 25 | 26 | handleURI(uri) { 27 | if (this._plugin == null) { 28 | this._createPlugin( 29 | uri.query.host, 30 | uri.query.port, 31 | uri.query.projectRoot, 32 | ); 33 | } 34 | }, 35 | 36 | toggle() { 37 | if (this._plugin == null) { 38 | this._createPlugin(); 39 | } else { 40 | this._disposePlugin(); 41 | } 42 | }, 43 | 44 | _createPlugin( 45 | initialHost = null, 46 | initialPort = null, 47 | initialProjectRoot = null, 48 | ) { 49 | if (this._plugin != null) { 50 | return; 51 | } 52 | this._plugin = new SkylinePlugin( 53 | initialHost, 54 | initialPort, 55 | initialProjectRoot, 56 | ); 57 | }, 58 | 59 | _disposePlugin() { 60 | if (this._plugin == null) { 61 | return; 62 | } 63 | this._plugin.dispose(); 64 | this._plugin = null; 65 | }, 66 | }; 67 | -------------------------------------------------------------------------------- /cli/skyline/analysis/static.py: -------------------------------------------------------------------------------- 1 | import ast 2 | import re 3 | 4 | END_OF_FUNCTION = re.compile('\):\s*$') 5 | 6 | 7 | class StaticAnalyzer: 8 | def __init__(self, source_code, source_tree): 9 | self._ast = source_tree 10 | self._code_by_line = source_code.splitlines() 11 | 12 | def batch_size_location(self): 13 | """ 14 | Locates the line of the 'batch_size' argument in the 15 | 'skyline_input_provider' function and determines if the provider's 16 | definition can be mutated using our heuristics. 17 | """ 18 | extractor = _InputProviderExtractor() 19 | extractor.visit(self._ast) 20 | function = extractor.function_node 21 | 22 | if (function is None or 23 | len(function.args.args) == 0 or 24 | function.args.args[0].arg != 'batch_size'): 25 | return None 26 | 27 | batch_size_line_number = function.args.args[0].lineno 28 | match = END_OF_FUNCTION.search(self._code_by_line[function.lineno - 1]) 29 | can_mutate = match is not None 30 | 31 | return batch_size_line_number, can_mutate 32 | 33 | 34 | class _InputProviderExtractor(ast.NodeVisitor): 35 | def __init__(self): 36 | self.function_node = None 37 | 38 | def visit_FunctionDef(self, node): 39 | if self.function_node is not None: 40 | # Return early if we've already found the provider 41 | return 42 | if node.name != 'skyline_input_provider': 43 | return 44 | self.function_node = node 45 | -------------------------------------------------------------------------------- /plugin/styles/PerfBar.less: -------------------------------------------------------------------------------- 1 | @import "ui-variables"; 2 | @import "innpv-animation"; 3 | @import "syntax-variables"; 4 | 5 | .innpv-perfbar-wrap { 6 | opacity: 0.75; 7 | transition-property: opacity, height; 8 | transition-duration: @perfbar-duration; 9 | transition-timing-function: @perfbar-easing; 10 | 11 | &.innpv-perfbar-active { 12 | opacity: 1 !important; 13 | } 14 | 15 | .innpv-perfbarcontainer-focusing & { 16 | opacity: 0.25; 17 | } 18 | } 19 | 20 | .innpv-perfbar-resizable { 21 | cursor: row-resize; 22 | } 23 | 24 | .innpv-perfbar-clickable { 25 | cursor: pointer; 26 | } 27 | 28 | .innpv-perfbar { 29 | height: 100%; 30 | } 31 | 32 | .innpv-line-highlight { 33 | background-color: @background-color-selected !important; 34 | } 35 | 36 | .innpv-blue-color-1 { 37 | background-color: #357586; 38 | } 39 | 40 | .innpv-blue-color-2 { 41 | background-color: #67bae0; 42 | } 43 | 44 | .innpv-blue-color-3 { 45 | background-color: #31659e; 46 | } 47 | 48 | .innpv-blue-color-4 { 49 | background-color: #a9bcd0; 50 | } 51 | 52 | .innpv-blue-color-5 { 53 | background-color: #5fafe1; 54 | } 55 | 56 | .innpv-green-color-1 { 57 | background-color: #19b571; 58 | } 59 | 60 | .innpv-green-color-2 { 61 | background-color: #88d498; 62 | } 63 | 64 | .innpv-green-color-3 { 65 | background-color: #4da167; 66 | } 67 | 68 | .innpv-green-color-4 { 69 | background-color: #a0e8af; 70 | } 71 | 72 | .innpv-green-color-5 { 73 | background-color: #38a700; 74 | } 75 | 76 | .innpv-untracked-color { 77 | background-color: @text-color-subtle; 78 | } 79 | -------------------------------------------------------------------------------- /plugin/lib/utils.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import path from 'path'; 4 | 5 | const BYTE_UNITS = [ 6 | 'B', 7 | 'KB', 8 | 'MB', 9 | 'GB', 10 | ]; 11 | 12 | export function processFileReference(fileReferenceProto) { 13 | return { 14 | filePath: path.join(...(fileReferenceProto.getFilePath().getComponentsList())), 15 | lineNumber: fileReferenceProto.getLineNumber(), 16 | }; 17 | }; 18 | 19 | export function toPercentage(numerator, denominator) { 20 | return numerator / denominator * 100; 21 | }; 22 | 23 | export function toReadableByteSize(sizeBytes) { 24 | let index = 0; 25 | let size = sizeBytes; 26 | for (; index < BYTE_UNITS.length; index++) { 27 | if (size < 1000) { 28 | break; 29 | } 30 | size /= 1024; 31 | } 32 | if (index == BYTE_UNITS.length) { 33 | index--; 34 | } 35 | 36 | return `${size.toFixed(1)} ${BYTE_UNITS[index]}`; 37 | }; 38 | 39 | export function scalePercentages({scaleSelector, shouldScale, applyFactor}) { 40 | return function(list, scaleFactor) { 41 | let total = 0; 42 | const adjusted = []; 43 | 44 | for (const element of list) { 45 | const value = scaleSelector(element); 46 | if (shouldScale(element)) { 47 | const scaled = value * scaleFactor; 48 | adjusted.push([scaled, element]); 49 | total += scaled; 50 | } else { 51 | adjusted.push([value, element]); 52 | total += value; 53 | } 54 | } 55 | 56 | return adjusted.map(([newValue, element]) => 57 | applyFactor(element, toPercentage(newValue, total))); 58 | }; 59 | }; 60 | -------------------------------------------------------------------------------- /website/src/components/CitationBlock.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import styles from './styles.module.css'; 3 | 4 | import CodeBlock from '@theme/CodeBlock'; 5 | import Tabs from '@theme/Tabs'; 6 | import TabItem from '@theme/TabItem'; 7 | 8 | const bibtexCitation = `@inproceedings{skyline-yu20, 9 | title = {{Skyline: Interactive In-Editor Computational Performance Profiling 10 | for Deep Neural Network Training}}, 11 | author = {Yu, Geoffrey X. and Grossman, Tovi and Pekhimenko, Gennady}, 12 | booktitle = {{Proceedings of the 33rd ACM Symposium on User Interface 13 | Software and Technology (UIST'20)}}, 14 | year = {2020}, 15 | }`; 16 | 17 | const textCitation = `Geoffrey X. Yu, Tovi Grossman, and Gennady Pekhimenko. 18 | Skyline: Interactive In-Editor Computational Performance Profiling for 19 | Deep Neural Network Training. In Proceedings of the 33rd ACM Symposium 20 | on User Interface Software and Technology (UIST'20). 2020.`; 21 | 22 | function CitationBlock() { 23 | return ( 24 |
25 | 32 | 33 | {bibtexCitation} 34 | 35 | 36 | {textCitation} 37 | 38 | 39 |
40 | ); 41 | } 42 | 43 | export default CitationBlock; 44 | -------------------------------------------------------------------------------- /plugin/lib/redux/reducers/state_transition.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import PerfVisState from '../../models/PerfVisState'; 4 | 5 | export default function transitionTo(nextPerfVisState, entireState) { 6 | if (nextPerfVisState === entireState.perfVisState) { 7 | // The next state is the same as the current state, so do nothing 8 | return {}; 9 | } 10 | 11 | switch (entireState.perfVisState) { 12 | case PerfVisState.SHOWING_PREDICTIONS: 13 | return fromShowingPredictions(nextPerfVisState, entireState); 14 | 15 | case PerfVisState.ERROR: 16 | return fromError(nextPerfVisState, entireState); 17 | 18 | case PerfVisState.EXPLORING_WEIGHTS: 19 | case PerfVisState.EXPLORING_OPERATIONS: 20 | return fromExploring(nextPerfVisState, entireState); 21 | 22 | default: 23 | return {perfVisState: nextPerfVisState}; 24 | } 25 | }; 26 | 27 | function fromShowingPredictions(nextPerfVisState, entireState) { 28 | return { 29 | perfVisState: nextPerfVisState, 30 | predictionModels: { 31 | ...entireState.predictionModels, 32 | currentBatchSize: null, 33 | undoCheckpoint: null, 34 | }, 35 | }; 36 | } 37 | 38 | function fromError(nextPerfVisState, entireState) { 39 | return { 40 | perfVisState: nextPerfVisState, 41 | errorMessage: '', 42 | errorFilePath: null, 43 | errorLineNumber: null, 44 | }; 45 | } 46 | 47 | function fromExploring(nextPerfVisState, entireState) { 48 | return { 49 | perfVisState: nextPerfVisState, 50 | breakdown: { 51 | ...entireState.breakdown, 52 | currentView: null, 53 | }, 54 | }; 55 | } 56 | -------------------------------------------------------------------------------- /cli/skyline/tracking/call_stack.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import inspect 3 | import os 4 | 5 | import torch 6 | 7 | SourceLocation = collections.namedtuple( 8 | 'SourceLocation', ['file_path', 'line_number', 'module_id']) 9 | 10 | 11 | class CallStack: 12 | def __init__(self, frames): 13 | self.frames = frames 14 | 15 | @staticmethod 16 | def from_here(project_root, start_from=1): 17 | """ 18 | Returns the current call stack when invoked. 19 | """ 20 | stack = inspect.stack() 21 | context = [] 22 | try: 23 | for frame_info in stack[start_from:]: 24 | # Only track source locations that are within the project and 25 | # that are within a torch.nn.Module. Note that we assume the 26 | # user uses "self" to refer to the current class instance. 27 | if not frame_info.filename.startswith(project_root): 28 | continue 29 | if 'self' not in frame_info.frame.f_locals: 30 | continue 31 | if not isinstance( 32 | frame_info.frame.f_locals['self'], torch.nn.Module): 33 | continue 34 | 35 | context.append(SourceLocation( 36 | file_path=os.path.relpath( 37 | frame_info.filename, start=project_root), 38 | line_number=frame_info.lineno, 39 | module_id=id(frame_info.frame.f_locals['self']), 40 | )) 41 | return CallStack(context) 42 | finally: 43 | del stack 44 | -------------------------------------------------------------------------------- /tools/shared.sh: -------------------------------------------------------------------------------- 1 | function pushd() { 2 | command pushd "$@" > /dev/null 3 | } 4 | 5 | function popd() { 6 | command popd "$@" > /dev/null 7 | } 8 | 9 | COLOR_RED="\033[0;31m" 10 | COLOR_GREEN="\033[0;32m" 11 | COLOR_YELLOW="\033[0;33m" 12 | COLOR_BLUE="\033[0;36m" 13 | COLOR_NC="\033[0m" 14 | 15 | function echo_colored() { 16 | echo -e "${1}${2}${COLOR_NC}" 17 | } 18 | 19 | function echo_green() { 20 | echo_colored "$COLOR_GREEN" "$1" 21 | } 22 | 23 | function echo_red() { 24 | echo_colored "$COLOR_RED" "$1" 25 | } 26 | 27 | function echo_yellow() { 28 | echo_colored "$COLOR_YELLOW" "$1" 29 | } 30 | 31 | function echo_blue() { 32 | echo_colored "$COLOR_BLUE" "$1" 33 | } 34 | 35 | function prompt_yn() { 36 | echo -en "${COLOR_YELLOW}$1${COLOR_NC}" 37 | read -r 38 | if [[ ! $REPLY =~ ^[Yy]$ ]] 39 | then 40 | exit 1 41 | fi 42 | } 43 | 44 | function get_monorepo_hash() { 45 | echo "$(git rev-parse HEAD)" 46 | } 47 | 48 | function get_monorepo_short_hash() { 49 | echo "$(git rev-parse --short HEAD)" 50 | } 51 | 52 | function check_monorepo() { 53 | # Make sure everything has been committed 54 | if [[ ! -z $(git status --porcelain) ]]; 55 | then 56 | echo_red "ERROR: There are uncommitted changes. Please commit before releasing." 57 | exit 1 58 | fi 59 | 60 | # Make sure we're on master 61 | INNPV_MASTER_HASH=$(git rev-parse master) 62 | INNPV_HASH=$(git rev-parse HEAD) 63 | 64 | if [[ $INNPV_MASTER_HASH != $INNPV_HASH ]]; then 65 | echo_red "ERROR: You must be on master when releasing." 66 | exit 1 67 | fi 68 | 69 | INNPV_SHORT_HASH=$(git rev-parse --short HEAD) 70 | 71 | echo_green "✓ Repository OK" 72 | } 73 | -------------------------------------------------------------------------------- /plugin/lib/redux/reducers/project.js: -------------------------------------------------------------------------------- 1 | 'use babel'; 2 | 3 | import { 4 | PROJECT_MODIFIED_CHANGE, 5 | PROJECT_EDITORS_CHANGE, 6 | PROJECT_CAN_PROFILE, 7 | } from '../actions/types'; 8 | import transitionTo from './state_transition'; 9 | import PerfVisState from '../../models/PerfVisState'; 10 | 11 | export default function(state, action) { 12 | switch (action.type) { 13 | case PROJECT_MODIFIED_CHANGE: { 14 | const {modifiedEditorsByFilePath} = action.payload; 15 | 16 | // By default, we consider the project to have modifications if there 17 | // exists one project editor with changes. 18 | let projectModified = modifiedEditorsByFilePath.size > 0; 19 | 20 | // However when predictions occur, we mutate the project. We don't want 21 | // to mark the project modified in this case. 22 | const {batchSizeContext, perfVisState} = state; 23 | if (perfVisState === PerfVisState.SHOWING_PREDICTIONS && 24 | batchSizeContext != null && 25 | modifiedEditorsByFilePath.size === 1 && 26 | modifiedEditorsByFilePath.has(batchSizeContext.filePath)) { 27 | projectModified = false; 28 | } 29 | 30 | return { 31 | ...state, 32 | projectModified, 33 | modifiedEditorsByPath: modifiedEditorsByFilePath, 34 | }; 35 | } 36 | 37 | case PROJECT_EDITORS_CHANGE: 38 | return { 39 | ...state, 40 | editorsByPath: action.payload.editorsByPath, 41 | }; 42 | 43 | case PROJECT_CAN_PROFILE: 44 | return { 45 | ...state, 46 | ...transitionTo(PerfVisState.READY_STALE, state), 47 | }; 48 | 49 | default: 50 | return state; 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /website/src/components/VideoOverlay.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import styles from './styles.module.css'; 4 | 5 | class VideoOverlay extends React.Component { 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | opacity: 0, 10 | }; 11 | } 12 | 13 | componentDidMount() { 14 | this.setState({opacity: 1}); 15 | } 16 | 17 | _computeDimensions() { 18 | const width = document.body.clientWidth * 0.8; 19 | const height = width / 16 * 9; 20 | return {width: Math.floor(width), height: Math.floor(height)}; 21 | } 22 | 23 | _renderBody() { 24 | const {width, height} = this._computeDimensions(); 25 | return ( 26 |
31 |
35 |