Skip to content

Instantly share code, notes, and snippets.

@lizmat
Created December 21, 2016 11:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lizmat/afb86eeb90576ed32cb3f2b361fa2870 to your computer and use it in GitHub Desktop.
Save lizmat/afb86eeb90576ed32cb3f2b361fa2870 to your computer and use it in GitHub Desktop.
variance in Num.floor performance in "my int $a; for <a b c>.roll(*) { last if ($a = $a + 1) > 100000 }"
<!DOCTYPE html>
<html lang="en" ng-app="moarProfApp" ng-controller="NavigationController">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MoarVM Profiler Results</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css">
<style>
body {
min-height: 500px;
padding-top: 70px;
}
.icyclegraph .call {
display: block;
border: 1px solid black;
overflow: hidden;
}
.icyclegraph .child {
float: left;
}
.icyclegraph a {
color: black;
}
</style>
</head>
<body >
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand">MoarVM Profiler Results</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li ng-class="Tab == 'Overview' ? 'active' : ''"><a href="#overview" ng-click="Tab = 'Overview'">Overview</a></li>
<li ng-class="Tab == 'Routines' ? 'active' : ''"><a href="#routines" ng-click="Tab = 'Routines'">Routines</a></li>
<li ng-class="Tab == 'Call Graph' ? 'active' : ''"><a href="#callgraph" ng-click="Tab = 'Call Graph'">Call Graph</a></li>
<li ng-class="Tab == 'Allocations' ? 'active' : ''"><a href="#allocations" ng-click="Tab = 'Allocations'">Allocations</a></li>
<li ng-class="Tab == 'GC' ? 'active' : ''"><a href="#gc" ng-click="Tab = 'GC'">GC</a></li>
<li ng-class="Tab == 'OSR/Deopt' ? 'active' : ''"><a href="#osrdeopt" ng-click="Tab = 'OSR/Deopt'">OSR / Deopt</a></li>
</ul>
</div>
</div>
</div>
<div class="container" ng-show="Tab == 'Overview'">
<div ng-controller="OverviewController">
<h3>Time Spent</h3>
<p>The profiled code ran for <strong>{{TotalTime}}ms</strong>. Of this,
<strong>{{OverheadTime}}ms</strong> were spent on garbage collection
and dynamic optimization (that's <strong>{{OverheadTimePercent}}%</strong>).
</p>
<table class="table table-striped table-condensed table-bordered">
<tbody>
<tr>
<td><strong>Executing Code</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ExecutingTimePercent}}%;">
</div>
</div>
</div>
<div>
{{ExecutingTimePercent}}%
({{ExecutingTime}}ms)
</div>
</td>
</tr>
<tr>
<td><strong>Garbage Collection</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{GCTimePercent}}%;">
</div>
</div>
</div>
<div>
{{GCTimePercent}}%
({{GCTime}}ms)
</div>
</td>
</tr>
<tr>
<td><strong>Dynamic Optimization</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{SpeshTimePercent}}%;">
</div>
</div>
</div>
<div>
{{SpeshTimePercent}}%
({{SpeshTime}}ms)
</div>
</td>
</tr>
</tbody>
</table>
<h3>Call Frames</h3>
<p>In total, <strong>{{EntriesWithoutInline}} call frames</strong> were
entered and exited by the profiled code. Inlining eliminated the need
to create <strong>{{EntriesInline}} call frames</strong> (that's
<strong>{{InlinePercent}}%</strong>).
</p>
<table class="table table-striped table-condensed table-bordered">
<tbody>
<tr>
<td><strong>Interpreted Frames</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{InterpFramesPercent}}%;">
</div>
</div>
</div>
<div>
{{InterpFramesPercent}}%
({{InterpFrames}})
</div>
</td>
</tr>
<tr>
<td><strong>Specialized Frames</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{SpeshFramesPercent}}%;">
</div>
</div>
</div>
<div>
{{SpeshFramesPercent}}%
({{SpeshFrames}})
</div>
</td>
</tr>
<tr>
<td><strong>JIT-Compiled Frames</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{JITFramesPercent}}%;">
</div>
</div>
</div>
<div>
{{JITFramesPercent}}%
({{JITFrames}})
</div>
</td>
</tr>
</tbody>
</table>
<h3>Garbage Collection</h3>
<p>The profiled code did <strong>{{GCRuns}} garbage collections</strong>.
There were <strong>{{FullGCRuns}} full collections</strong> involving
the entire heap.
</p>
<p ng-show="{{GCRuns > 0}}">
The average nursery collection time was <strong>{{NurseryAverage}}ms</strong>.
<span ng-show="{{FullGCRuns > 0}}">
The average full collection time was <strong>{{FullAverage}}ms</strong>.
</span>
</p>
<h3>Dynamic Optimization</h3>
<p>Of {{OptimizedFrames}} specialized or JIT-compiled frames, there were
<strong>{{DeoptOnes}} deoptimizations</strong> (that's <strong>
{{DeoptOnePercent}}%</strong> of all optimized frames).
</p>
<p ng-show="DeoptAlls == 0">
There was <strong>no global deoptimization</strong> triggered by the
profiled code.
</p>
<p ng-show="DeoptAlls == 1">
There was <strong>one global deoptimization</strong> triggered by the
profiled code.
</p>
<p ng-show="DeoptAlls > 1">
There were <strong>{{DeoptAlls}} global deoptimization</strong> triggered
by the profiled code.
</p>
<p ng-show="OSRs == 0">
There was <strong>no On Stack Replacement</strong> performed while
executing the profiled code (normal if the code lacks long-running
loops with many iterations).
</p>
<p ng-show="OSRs == 1">
There was <strong>one On Stack Replacement</strong> performed while
executing the profiled code.
</p>
<p ng-show="OSRs > 1">
There were <strong>{{OSRs}} On Stack Replacements</strong> performed
while executing the profiled code.
</p>
</div>
</div>
<div class="container" ng-show="Tab == 'Routines'">
<div ng-controller="RoutinesController">
<table class="table table-striped table-condensed table-bordered">
<thead>
<th>
<a href="" ng-click="reverse = predicate == 'Name' ? !reverse : false; predicate = 'Name';">Name</a>
<input ng-model="NameFilter">
</th>
<th><a href="" ng-click="reverse = predicate == 'Entries' ? !reverse : true; predicate = 'Entries';">Entries</a></th>
<th><a href="" ng-click="reverse = predicate == 'InclusiveTimePercent' ? !reverse : true; predicate = 'InclusiveTimePercent';">Inclusive Time</a></th>
<th><a href="" ng-click="reverse = predicate == 'ExclusiveTimePercent' ? !reverse : true; predicate = 'ExclusiveTimePercent';">Exclusive Time</a></th>
<th>
<span class="text-danger" tooltip-placement="bottom" tooltip="Unoptimized interpreted code">Interp</span> /
<span class="text-warning" tooltip-placement="bottom" tooltip="Type-specialized interpreted code">Spesh</span> /
<span class="text-success" tooltip-placement="bottom" tooltip="Type-specialized, JIT-compiled code">JIT</span>
</th>
</thead>
<tbody>
<tr ng-repeat="routine in Routines | filter:NameFilter | orderBy:predicate:reverse">
<td>
<strong>{{routine.Name}}</strong><br />
<span class="text-muted">{{routine.File}}:{{routine.Line}}</span>
</td>
<td>{{routine.Entries}}</td>
<td>
<div class="pull-left" style="width: 70px">
<div class="progress" style="width: 60px">
<div class="progress-bar" role="progressbar" style="width: {{routine.InclusiveTimePercent}}%;">
</div>
</div>
</div>
<div>
<strong>{{routine.InclusiveTimePercent}}%</strong>
({{routine.InclusiveTime}}ms)
</div>
</td>
<td>
<div class="pull-left" style="width: 70px">
<div class="progress" style="width: 60px">
<div class="progress-bar" role="progressbar" style="width: {{routine.ExclusiveTimePercent}}%;">
</div>
</div>
</div>
<div>
<strong>{{routine.ExclusiveTimePercent}}%</strong>
({{routine.ExclusiveTime}}ms)
</div>
</td>
<td>
<div class="pull-left" style="width: 110px">
<div class="progress" style="width: 100px">
<div class="progress-bar progress-bar-danger" style="width: {{routine.InterpEntriesPercent}}%">
</div>
<div class="progress-bar progress-bar-warning" style="width: {{routine.SpeshEntriesPercent}}%">
</div>
<div class="progress-bar progress-bar-success" style="width: {{routine.JITEntriesPercent}}%">
</div>
</div>
</div>
<div>
<span class="label label-default" ng-show="routine.OSR">OSR</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<script type="text/ng-template" id="icycle_graph_callee_renderer.html">
<a href="#" class="call" style="background-color:{{backgroundColor(callee)}}" ng-click="toCallee(callee)">{{callee.name}}</a>
<div class="child" style="width: {{callee.inclusive_time * 100 / Current.inclusive_time}}%;" ng-repeat="callee in callee.callees" title="{{callee.name}}" ng-include="'icycle_graph_callee_renderer.html'"></div>
</script>
<div class="container" ng-show="Tab == 'Call Graph'">
<div ng-controller="CallGraphController">
<ol class="breadcrumb">
<li><strong>Callers:</strong></li>
<li ng-show="RecentCallers.length == 0"><em>None</em></li>
<li ng-repeat="caller in RecentCallers">
<a href="" ng-click="toCaller(caller)">
{{caller.name == '' ? '&lt;anon&gt;' : caller.name}}
</a>
</li>
</ol>
<p>
<span class="lead">{{Current.name == '' ? '&lt;anon&gt;' : Current.name}}</span><br />
<span class="text-muted">{{File}}:{{Line}}</span>
</p>
<div class="icyclegraph">
<div class="child" ng-repeat="callee in [Current]" ng-include="'icycle_graph_callee_renderer.html'"></div>
</div>
<table class="table table-striped table-condensed table-bordered">
<tbody>
<tr>
<td><strong>Calls <span class="text-success">(Inlined)</span></strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar" role="progressbar" style="width: {{Percent}}%;">
</div>
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{InlinePercent}}%;">
</div>
</div>
</div>
<div ng-show="InlinePercent == 0">
{{Entries}}
</div>
<div ng-show="InlinePercent != 0">
{{Entries}} +
<span class="text-success">{{InlineEntries}} ({{InlinePercent}}%)</span>
</div>
</td>
</tr>
<tr>
<td><strong>Interpreted Calls</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{InterpPercent}}%;">
</div>
</div>
</div>
<div>
{{InterpPercent}}%
({{InterpEntries}})
</div>
</td>
</tr>
<tr>
<td><strong>Specialized Calls</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{SpeshPercent}}%;">
</div>
</div>
</div>
<div>
{{SpeshPercent}}%
({{SpeshEntries}})
</div>
</td>
</tr>
<tr>
<td><strong>JIT-Compiled Calls</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{JITPercent}}%;">
</div>
</div>
</div>
<div>
{{JITPercent}}%
({{JITEntries}})
</div>
</td>
</tr>
</tbody>
</table>
<div class="panel panel-default">
<div class="panel-heading">Callees</div>
<table class="table table-striped table-condensed table-bordered" ng-show="Callees.length > 0">
<thead>
<th>
<a href="" ng-click="reverse = predicate == 'Name' ? !reverse : false; predicate = 'Name';">Name</a>
<input ng-model="NameFilter">
</th>
<th><a href="" ng-click="reverse = predicate == 'Calls' ? !reverse : true; predicate = 'Calls';">Calls</a></th>
<th><a href="" ng-click="reverse = predicate == 'TimePercent' ? !reverse : true; predicate = 'TimePercent';">Time In Callee</a></th>
<th>
<span class="text-danger" tooltip-placement="bottom" tooltip="Unoptimized interpreted code">Interp</span> /
<span class="text-warning" tooltip-placement="bottom" tooltip="Type-specialized interpreted code">Spesh</span> /
<span class="text-success" tooltip-placement="bottom" tooltip="Type-specialized, JIT-compiled code">JIT</span>
</th>
<th>
<span tooltip-placement="bottom" tooltip="Code from this callee was flattened into the routine by the optimizer">Inlined</span>
</th>
</thead>
<tbody>
<tr ng-repeat="callee in Callees | filter:NameFilter | orderBy:predicate:reverse">
<td>
<strong><a href="" ng-click="toCallee(callee.Node)">{{callee.Name}}</a></strong><br />
<span class="text-muted">{{callee.File}}:{{callee.Line}}</span>
</td>
<td>{{callee.Calls}}</td>
<td>
<div class="pull-left" style="width: 70px">
<div class="progress" style="width: 60px">
<div class="progress-bar" role="progressbar" style="width: {{callee.TimePercent}}%;">
</div>
</div>
</div>
<div>
<strong>{{callee.TimePercent}}%</strong>
({{callee.Time}}ms)
</div>
</td>
<td>
<div class="progress" style="width: 100px">
<div class="progress-bar progress-bar-danger" style="width: {{callee.InterpCallsPercent}}%">
</div>
<div class="progress-bar progress-bar-warning" style="width: {{callee.SpeshCallsPercent}}%">
</div>
<div class="progress-bar progress-bar-success" style="width: {{callee.JITCallsPercent}}%">
</div>
</div>
</td>
<td>
<div ng-show="callee.VeryInline">
<span class="text-success">
<span class="glyphicon glyphicon-star"></span>
</span>
{{callee.InlinedPercent}}%
</div>
<div ng-show="callee.SometimesInline">
<span class="text-muted">
<span class="glyphicon glyphicon-star-empty"></span>
</span>
{{callee.InlinedPercent}}%
</div>
</td>
</tr>
</tbody>
</table>
<div class="panel-body" ng-show="Callees.length == 0">
This code has no callees.
</div>
</div>
</div>
</div>
<div class="container" ng-show="Tab == 'Allocations'">
<div ng-controller="AllocationsController">
<table class="table table-striped table-condensed table-bordered">
<thead>
<th>
<a href="" ng-click="reverse = predicate == 'Name' ? !reverse : false; predicate = 'Name';">Name</a>
<input ng-model="NameFilter">
</th>
<th><a href="" ng-click="reverse = predicate == 'Allocations' ? !reverse : true; predicate = 'Allocations';">Allocations</a></th>
<th>Allocating Routines</th>
</thead>
<tbody>
<tr ng-repeat="alloc in AllocationSummary | filter:NameFilter | orderBy:predicate:reverse">
<td><strong>{{alloc.Name}}</strong></td>
<td>
<div class="pull-left" style="width: 210px">
<div class="progress" style="width: 200px">
<div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{alloc.AllocationsInterpPercent}}%;">
</div>
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{alloc.AllocationsSpeshPercent}}%;">
</div>
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{alloc.AllocationsJitPercent}}%;">
</div>
</div>
</div>
<div>
{{alloc.Allocations}}
</div>
</td>
<td>
<a href="" ng-click="showAllocatingRoutines(alloc)">View</a>
</td>
</tr>
</tbody>
</table>
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<h4 class="modal-title" id="allocatingRoutinesModalLabel">
Routines Allocating {{CurrentAllocatingRoutine}}
</h4>
</div>
<div class="modal-body">
<table class="table table-striped table-condensed table-bordered">
<thead>
<th>
<a href="" ng-click="routineReverse = routinePredicate == 'Name' ? !routineReverse : false; routinePredicate = 'Name';">Name</a>
<input ng-model="RoutineNameFilter">
</th>
<th><a href="" ng-click="routineReverse = routinePredicate == 'Allocations' ? !routineReverse : true; routinePredicate = 'Allocations';">Allocations</a></th>
</thead>
<tbody>
<tr ng-repeat="routine in CurrentAllocatingRoutineStats | filter:RoutineNameFilter | orderBy:routinePredicate:routineReverse">
<td>
<strong>{{routine.Name}}</strong><br />
<span class="text-muted">{{routine.File}}:{{routine.Line}}</span>
</td>
<td>
<div class="pull-left" style="width: 210px">
<div class="progress" style="width: 200px">
<div class="progress-bar progress-bar-danger" style="width: {{routine.AllocationsInterpPercent}}%;">
</div>
<div class="progress-bar progress-bar-warning" style="width: {{routine.AllocationsSpeshPercent}}%;">
</div>
<div class="progress-bar progress-bar-success" style="width: {{routine.AllocationsJitPercent}}%;">
</div>
</div>
</div>
<div>
{{routine.Allocations}}
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</script>
</div>
</div>
<div class="container" ng-show="Tab == 'GC'">
<div ng-controller="GCController">
<table class="table table-striped table-condensed table-bordered">
<thead>
<th>
<a href="" ng-click="reverse = predicate == 'Run' ? !reverse : false; predicate = 'Run';">Run</a>
</th>
<th>Full</th>
<th>
<a href="" ng-click="reverse = predicate == 'Time' ? !reverse : false; predicate = 'Time';">Time</a>
</th>
<th>
Nursery:
<span class="text-danger" tooltip-placement="bottom" tooltip="Bytes retained in the nursery">Retained</span> /
<span class="text-warning" tooltip-placement="bottom" tooltip="Bytes promoted to gen2 and now available in nursery">Promoted</span> /
<span class="text-success" tooltip-placement="bottom" tooltip="Bytes released and now availabe in nursery">Freed</span>
</th>
</thead>
<tbody>
<tr ng-repeat="gc in GCs | orderBy:predicate:reverse">
<td><strong>{{gc.Run}}</strong></td>
<td>
<span class="text-success" ng-show="gc.Full">
<span class="glyphicon glyphicon-star"></span>
</span>
</td>
<td>
<div class="pull-left" style="width: 210px">
<div class="progress" style="width: 200px">
<div class="progress-bar" role="progressbar" style="width: {{gc.TimePercent}}%;">
</div>
</div>
</div>
<div>
{{gc.Time}}ms
</div>
</td>
<td>
<div class="pull-left" style="width: 210px">
<div class="progress" style="width: 200px">
<div class="progress-bar progress-bar-danger" style="width: {{gc.RetainedPercent}}%">
</div>
<div class="progress-bar progress-bar-warning" style="width: {{gc.PromotedPercent}}%">
</div>
<div class="progress-bar progress-bar-success" style="width: {{gc.ClearedPercent}}%">
</div>
</div>
</div>
<div>
{{gc.RetainedKilobytes}}KB /
{{gc.PromotedKilobytes}}KB /
{{gc.ClearedKilobytes}}KB ...
<small>{{gc.Gen2Roots}} gen2 roots</small>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="container" ng-show="Tab == 'OSR/Deopt'">
<div ng-controller="OSRDeoptController">
<h3>OSR</h3>
<p>On Stack Replacement detects routines containing hot loops that are
being interpreted, and replaces them with specialized or JIT-compiled
code.</p>
<table class="table table-striped table-condensed table-bordered" ng-show="OSRs.length > 0">
<thead>
<th>
Routine
</th>
<th>On Stack Replacements</th>
</thead>
<tbody>
<tr ng-repeat="osr in OSRs | orderBy:predicate:reverse">
<td>
<strong>{{osr.Name}}</strong><br />
<span class="text-muted">{{osr.File}}:{{osr.Line}}</span>
</td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar" role="progressbar" style="width: {{osr.Percent}}%;">
</div>
</div>
</div>
<div>
{{osr.Count}}
</div>
</td>
</tr>
</tbody>
</table>
<p ng-show="OSRs.length == 0">
<em>No OSR was performed during this profile.</em>
</p>
<h3>Local Deoptimization</h3>
<p>Local deoptimization happens when a guard in specialized or JIT-compiled
code fails. Since the code was produced assuming the guard would hold,
the VM falls back to running the safe, but slower, interpreted code.</p>
<table class="table table-striped table-condensed table-bordered" ng-show="DeoptOnes.length > 0">
<thead>
<th>
Routine
</th>
<th>Deoptimizations</th>
</thead>
<tbody>
<tr ng-repeat="deopt in DeoptOnes | orderBy:predicate:reverse">
<td>
<strong>{{deopt.Name}}</strong><br />
<span class="text-muted">{{deopt.File}}:{{deopt.Line}}</span>
</td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar" role="progressbar" style="width: {{deopt.Percent}}%;">
</div>
</div>
</div>
<div>
{{deopt.Count}}
</div>
</td>
</tr>
</tbody>
</table>
<p ng-show="DeoptOnes.length == 0">
<em>No local deoptimizations occurred during this profile.</em>
</p>
<h3>Global Deoptimization</h3>
<p>Global deoptimization happens when an event occurs that renders
all currently type-specialized or JIT-compiled code on the call
stack potentially invalid. Mixins - changing the type of an object
in place - are a common reason.</p>
<table class="table table-striped table-condensed table-bordered" ng-show="DeoptAlls.length > 0">
<thead>
<th>
Routine
</th>
<th>Deoptimizations</th>
</thead>
<tbody>
<tr ng-repeat="deopt in DeoptAlls | orderBy:predicate:reverse">
<td>
<strong>{{deopt.Name}}</strong><br />
<span class="text-muted">{{deopt.File}}:{{deopt.Line}}</span>
</td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar" role="progressbar" style="width: {{deopt.Percent}}%;">
</div>
</div>
</div>
<div>
{{deopt.Count}}
</div>
</td>
</tr>
</tbody>
</table>
<p ng-show="DeoptAlls.length == 0">
<em>No global deoptimizations occurred during this profile.</em>
</p>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.2.0/ui-bootstrap-tpls.js"></script>
<script>
var rawData = JSON.parse('[{"9":"List","8":{"name":"","line":3152,"file":"gen/moar/BOOTSTRAP.nqp"},"7":"CallCapture","6":"BOOTHash","57":{"name":"sink","line":13,"file":"SETTING::src/core/Mu.pm"},"56":{"name":"","line":-1,"file":"/Users/liz/Github/rakudo.moar/install/share/perl6/runtime/CORE.setting.moarvm"},"55":{"name":"last","line":77,"file":"SETTING::src/core/control.pm"},"54":{"name":"last","line":76,"file":"SETTING::src/core/control.pm"},"53":{"name":"","line":1,"file":"-e"},"52":"Int","51":{"name":"floor","line":135,"file":"SETTING::src/core/Num.pm"},"50":{"name":"rand","line":126,"file":"SETTING::src/core/Num.pm"},"5":{"name":"roll","line":1079,"file":"SETTING::src/core/List.pm"},"49":"Num","48":{"name":"Bridge","line":54,"file":"SETTING::src/core/Int.pm"},"47":{"name":"rand","line":9,"file":"SETTING::src/core/Real.pm"},"46":{"name":"","line":1093,"file":"SETTING::src/core/List.pm"},"45":{"name":"pull-one","line":962,"file":"SETTING::src/core/Rakudo/Internals.pm"},"44":{"name":"","line":448,"file":"SETTING::src/core/Any-iterable-methods.pm"},"43":{"name":"sink-all","line":440,"file":"SETTING::src/core/Any-iterable-methods.pm"},"42":{"name":"sink","line":158,"file":"SETTING::src/core/Seq.pm"},"41":"Any::IterateOneWithoutPhasers","40":{"name":"new","line":334,"file":"SETTING::src/core/Any-iterable-methods.pm"},"4":{"name":"<unit>","line":1,"file":"-e"},"39":{"name":"","line":1437,"file":"gen/moar/BOOTSTRAP.nqp"},"38":{"name":"returns","line":7,"file":"SETTING::src/core/Block.pm"},"37":{"name":"infix:«<»","line":338,"file":"SETTING::src/core/Int.pm"},"36":{"name":"has-phasers","line":36,"file":"SETTING::src/core/Block.pm"},"35":{"name":"count","line":13,"file":"SETTING::src/core/Code.pm"},"34":{"name":"sequential-map","line":779,"file":"SETTING::src/core/Any-iterable-methods.pm"},"33":{"name":"type_check","line":2659,"file":"gen/moar/Metamodel.nqp"},"32":{"name":"iterator","line":52,"file":"SETTING::src/core/Seq.pm"},"31":{"name":"map","line":17,"file":"SETTING::src/core/Any-iterable-methods.pm"},"30":{"name":"map","line":11,"file":"SETTING::src/core/Any-iterable-methods.pm"},"3":"BOOTArray","29":"Seq","28":{"name":"new","line":42,"file":"SETTING::src/core/Seq.pm"},"27":"<anon|140492422530512>","26":{"name":"new","line":959,"file":"SETTING::src/core/Rakudo/Internals.pm"},"25":{"name":"","line":957,"file":"SETTING::src/core/Rakudo/Internals.pm"},"24":{"name":"UnendingCallableIterator","line":956,"file":"SETTING::src/core/Rakudo/Internals.pm"},"23":"Block","22":{"name":"","line":1660,"file":"gen/moar/BOOTSTRAP.nqp"},"21":{"name":"elems","line":536,"file":"SETTING::src/core/List.pm"},"20":{"name":"is-lazy","line":1001,"file":"SETTING::src/core/List.pm"},"2":"BOOTCode","19":"Scalar","18":{"name":"roll","line":1087,"file":"SETTING::src/core/List.pm"},"17":{"name":"add_to_cache","line":2451,"file":"gen/moar/BOOTSTRAP.nqp"},"16":{"name":"is_narrower","line":1853,"file":"gen/moar/BOOTSTRAP.nqp"},"15":"BOOTIntArray","14":"NQPArrayIter","13":{"name":"","line":1823,"file":"gen/moar/BOOTSTRAP.nqp"},"12":"BOOTInt","11":"NQPArray","10":{"name":"","line":2167,"file":"gen/moar/BOOTSTRAP.nqp"},"1":{"name":"<unit-outer>","line":1,"file":"-e"},"0":{"name":"","line":1570,"file":"gen/moar/stage2/NQPHLL.nqp"}},{"total_time":259159,"spesh_time":1485,"gcs":[{"time":10285,"retained_bytes":1040272,"promoted_bytes":0,"gen2_roots":12677,"full":0,"cleared_bytes":3154024},{"time":11431,"retained_bytes":40,"promoted_bytes":6137103,"gen2_roots":17186,"full":0,"cleared_bytes":4293024425},{"time":11876,"retained_bytes":40,"promoted_bytes":0,"gen2_roots":2068,"full":0,"cleared_bytes":4194240}],"call_graph":{"inclusive_time":223393,"id":"0","exclusive_time":9,"entries":1,"callees":[{"inclusive_time":223383,"id":"1","exclusive_time":17,"entries":1,"callees":[{"inclusive_time":223366,"id":"4","exclusive_time":34,"entries":1,"callees":[{"inclusive_time":547,"id":"5","exclusive_time":149,"entries":1,"callees":[{"inclusive_time":0,"id":"8","exclusive_time":0,"entries":1,"allocations":[{"id":"9","count":1}]},{"inclusive_time":227,"id":"10","exclusive_time":83,"entries":1,"callees":[{"inclusive_time":143,"id":"13","exclusive_time":136,"entries":1,"callees":[{"inclusive_time":6,"id":"16","exclusive_time":6,"entries":6}],"allocations":[{"id":"11","count":17},{"id":"14","count":4},{"id":"6","count":6},{"id":"15","count":6},{"id":"12","count":3}]},{"inclusive_time":0,"id":"17","exclusive_time":0,"entries":1}],"allocations":[{"id":"2","count":1},{"id":"11","count":2},{"id":"6","count":1},{"id":"12","count":1}]},{"inclusive_time":169,"id":"18","exclusive_time":82,"entries":1,"callees":[{"inclusive_time":1,"id":"20","exclusive_time":1,"entries":1,"allocations":[{"id":"6","count":1},{"id":"19","count":1}]},{"inclusive_time":0,"id":"21","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1}]},{"inclusive_time":1,"id":"22","exclusive_time":1,"entries":1,"allocations":[{"id":"23","count":1},{"id":"2","count":1}]},{"inclusive_time":83,"id":"24","exclusive_time":80,"entries":1,"callees":[{"inclusive_time":1,"id":"25","exclusive_time":1,"entries":1,"allocations":[{"id":"19","count":1}]},{"inclusive_time":1,"id":"26","exclusive_time":1,"entries":1,"allocations":[{"id":"6","count":1},{"id":"27","count":1}]}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":0,"id":"28","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1},{"id":"29","count":1}]}],"allocations":[{"id":"6","count":1},{"id":"19","count":1}]}],"allocations":[{"id":"6","count":1},{"id":"3","count":1},{"id":"7","count":3}]},{"inclusive_time":0,"id":"22","exclusive_time":0,"entries":1,"allocations":[{"id":"23","count":1},{"id":"2","count":1}]},{"inclusive_time":119,"id":"30","exclusive_time":14,"entries":1,"callees":[{"inclusive_time":0,"id":"8","exclusive_time":0,"entries":1,"allocations":[{"id":"9","count":1}]},{"inclusive_time":10,"id":"10","exclusive_time":10,"entries":1,"callees":[{"inclusive_time":0,"id":"17","exclusive_time":0,"entries":1}],"allocations":[{"id":"2","count":1},{"id":"11","count":2},{"id":"6","count":1},{"id":"12","count":1}]},{"inclusive_time":93,"id":"31","exclusive_time":36,"entries":1,"callees":[{"inclusive_time":13,"id":"32","exclusive_time":13,"entries":1,"callees":[{"inclusive_time":0,"id":"33","exclusive_time":0,"entries":1}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":43,"id":"34","exclusive_time":34,"entries":1,"callees":[{"inclusive_time":0,"id":"35","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1}]},{"inclusive_time":0,"id":"36","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1}]},{"inclusive_time":0,"id":"37","exclusive_time":0,"entries":1},{"inclusive_time":4,"id":"38","exclusive_time":4,"entries":1,"callees":[{"inclusive_time":0,"id":"39","exclusive_time":0,"entries":1}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":1,"id":"40","exclusive_time":1,"entries":1,"allocations":[{"id":"19","count":2},{"id":"6","count":1},{"id":"41","count":1}]},{"inclusive_time":0,"id":"28","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1},{"id":"29","count":1}]}],"allocations":[{"id":"19","count":2}]}],"allocations":[{"id":"19","count":2},{"id":"6","count":1}]}],"allocations":[{"id":"3","count":1},{"id":"7","count":3}]},{"inclusive_time":222664,"id":"42","exclusive_time":26,"entries":1,"callees":[{"inclusive_time":1,"id":"32","exclusive_time":1,"entries":1,"callees":[{"inclusive_time":0,"id":"33","exclusive_time":0,"entries":1}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":222634,"id":"43","exclusive_time":13,"entries":1,"callees":[{"inclusive_time":0,"id":"22","exclusive_time":0,"entries":1,"allocations":[{"id":"23","count":1},{"id":"2","count":1}]},{"osr":1,"jit_entries":1,"inclusive_time":222620,"id":"44","exclusive_time":36572,"entries":1,"callees":[{"jit_entries":99844,"inclusive_time":180059,"id":"45","exclusive_time":19367,"entries":100001,"callees":[{"jit_entries":99844,"inclusive_time":160691,"id":"46","exclusive_time":41156,"entries":100001,"callees":[{"spesh_entries":99844,"inclusive_time":57240,"id":"47","exclusive_time":35025,"entries":100001,"callees":[{"spesh_entries":99844,"inclusive_time":10623,"id":"48","exclusive_time":10623,"entries":100001,"allocations":[{"id":"6","count":157},{"spesh":99844,"id":"49","count":100001}]},{"spesh_entries":99844,"inclusive_time":11591,"id":"50","exclusive_time":11591,"entries":100001,"allocations":[{"id":"6","count":157},{"spesh":99844,"id":"49","count":100001}]}],"allocations":[{"id":"6","count":157}]},{"spesh_entries":99844,"inclusive_time":62295,"id":"51","exclusive_time":62295,"entries":100001,"allocations":[{"id":"6","count":157},{"spesh":99844,"id":"52","count":100001}]}],"allocations":[{"id":"19","count":1}]}],"allocations":[{"id":"6","count":157}]},{"jit_entries":99844,"inclusive_time":5989,"id":"53","exclusive_time":5821,"entries":100001,"callees":[{"inclusive_time":167,"id":"54","exclusive_time":64,"entries":1,"callees":[{"inclusive_time":12,"id":"10","exclusive_time":11,"entries":1,"callees":[{"inclusive_time":1,"id":"17","exclusive_time":1,"entries":1}],"allocations":[{"id":"2","count":1},{"id":"11","count":2},{"id":"6","count":1},{"id":"12","count":1}]},{"inclusive_time":90,"id":"55","exclusive_time":87,"entries":1,"callees":[{"inclusive_time":3,"id":"56","exclusive_time":3,"entries":1,"allocations":[{"id":"52","count":1}]}]}],"allocations":[{"id":"7","count":3}]}]}],"allocations":[{"id":"19","count":1}]}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":1,"id":"57","exclusive_time":1,"entries":1,"allocations":[{"id":"6","count":1}]}],"allocations":[{"id":"6","count":1}]}]}],"allocations":[{"id":"2","count":1},{"id":"3","count":1}]}]}}]');
// grab the "dictionary" from the very first entry
var id_to_things = rawData[0];
// the rest of the program expects the list to contain callframes, not the dictionary
// so we just shift it off the beginning.
rawData.shift();
// Extract some common things out of the raw data.
var nodeIdToName = {};
var nodeIdToFile = {};
var nodeIdToLine = {};
(function () {
function walkCallGraphNode(node) {
if (!nodeIdToName[node.id]) {
var existing_data = id_to_things[node.id.toString()];
node.name = existing_data.name;
node.line = existing_data.line;
node.file = existing_data.file;
nodeIdToName[node.id] = node.name == "" ? "<anon>" : node.name;
nodeIdToLine[node.id] = node.line < 1 ? "<unknown>" : node.line;
nodeIdToFile[node.id] = node.file == "" ? "<unknown>" : node.file;
}
if (node.callees)
node.callees.map(walkCallGraphNode);
}
walkCallGraphNode(rawData[0].call_graph);
}());
// Register application and add controllers, increase recursion depth for callGraphController
var moarProfApp = angular.module('moarProfApp', ['ui.bootstrap']).config(function($rootScopeProvider) {
$rootScopeProvider.digestTtl(100);
});
moarProfApp.controller('NavigationController', function ($scope) {
$scope.Tab = 'Overview';
});
moarProfApp.controller('OverviewController', function ($scope) {
var totalTime = rawData[0].total_time;
var speshTime = rawData[0].spesh_time;
var gcTime = 0;
var gcNursery = 0;
var gcFull = 0;
var gcNurseryTime = 0;
var gcFullTime = 0;
var totalEntries = 0;
var inlineEntries = 0;
var jitEntries = 0;
var speshEntries = 0;
var deoptOnes = 0;
var deoptAlls = 0;
var osrs = 0;
rawData[0].gcs.map(function (gc) {
gcTime += gc.time;
if (gc.full) {
gcFull++;
gcFullTime += gc.time;
}
else {
gcNursery++;
gcNurseryTime += gc.time;
}
});
function walkCallGraphNode(node) {
totalEntries += node.entries || 0;
inlineEntries += node.inlined_entries || 0;
speshEntries += node.spesh_entries || 0;
jitEntries += node.jit_entries || 0;
deoptOnes += node.deopt_one || 0;
deoptAlls += node.deopt_all || 0;
osrs += node.osr || 0;
if (node.callees)
node.callees.map(walkCallGraphNode);
}
walkCallGraphNode(rawData[0].call_graph);
// Time spent
var overheadTime = speshTime + gcTime;
var executingTime = totalTime - overheadTime;
$scope.TotalTime = +(totalTime / 1000).toFixed(2);
$scope.OverheadTime = +(overheadTime / 1000).toFixed(2);
$scope.OverheadTimePercent = +(100 * overheadTime / totalTime).toFixed(2);
$scope.ExecutingTime = +(executingTime / 1000).toFixed(2);
$scope.ExecutingTimePercent = +(100 * executingTime / totalTime).toFixed(2);
$scope.GCTime = +(gcTime / 1000).toFixed(2);
$scope.GCTimePercent = +(100 * gcTime / totalTime).toFixed(2);
$scope.SpeshTime = +(speshTime / 1000).toFixed(2);
$scope.SpeshTimePercent = +(100 * speshTime / totalTime).toFixed(2);
// Routines
var interpEntries = totalEntries - (jitEntries + speshEntries);
$scope.EntriesWithoutInline = totalEntries - inlineEntries;
$scope.EntriesInline = inlineEntries;
$scope.InlinePercent = +(100 * inlineEntries / totalEntries).toFixed(2);
$scope.InterpFrames = interpEntries;
$scope.InterpFramesPercent = +(100 * interpEntries / totalEntries).toFixed(2);
$scope.SpeshFrames = speshEntries;
$scope.SpeshFramesPercent = +(100 * speshEntries / totalEntries).toFixed(2);
$scope.JITFrames = jitEntries;
$scope.JITFramesPercent = +(100 * jitEntries / totalEntries).toFixed(2);
// Garbage collection
$scope.GCRuns = gcNursery + gcFull;
$scope.FullGCRuns = gcFull;
$scope.NurseryAverage = +((gcNurseryTime / 1000) / gcNursery).toFixed(2);
$scope.FullAverage = +((gcFullTime / 1000) / gcFull).toFixed(2);
// Dynamic optimization
var optimizedFrames = speshEntries + jitEntries;
$scope.OptimizedFrames = optimizedFrames;
$scope.DeoptOnes = deoptOnes;
$scope.DeoptOnePercent = +(100 * deoptOnes / (optimizedFrames || 1)).toFixed(2);
$scope.DeoptAlls = deoptAlls;
$scope.OSRs = osrs;
});
moarProfApp.controller('RoutinesController', function ($scope) {
// Walk call graph to build data.
var idToEntries = {};
var idToSpeshEntries = {};
var idToJITEntries = {};
var idToExclusive = {};
var idToInclusive = {};
var idToOSR = {};
var idRecDepth = {};
var totalExclusive = 0;
var totalInclusive = rawData[0].call_graph.inclusive_time;
function walkCallGraphNode(node) {
if (!idToEntries[node.id]) {
idToEntries[node.id] = 0;
idToSpeshEntries[node.id] = 0;
idToJITEntries[node.id] = 0;
idToExclusive[node.id] = 0;
idToInclusive[node.id] = 0;
idToOSR[node.id] = false;
idRecDepth[node.id] = 0;
}
idToEntries[node.id] += node.entries || 0;
idToSpeshEntries[node.id] += node.spesh_entries || 0;
idToJITEntries[node.id] += node.jit_entries || 0;
idToExclusive[node.id] += node.exclusive_time || 0;
totalExclusive += node.exclusive_time || 0;
if (node.osr > 0)
idToOSR[node.id] = true;
if (idRecDepth[node.id] == 0)
idToInclusive[node.id] += node.inclusive_time;
if (node.callees) {
idRecDepth[node.id]++;
node.callees.map(walkCallGraphNode);
idRecDepth[node.id]--;
}
}
walkCallGraphNode(rawData[0].call_graph);
// Build object list per routine.
var routineList = [];
for (id in idToEntries) {
var speshEntriesPercent = +(100 * idToSpeshEntries[id] / idToEntries[id]).toFixed(2);
var jitEntriesPercent = +(100 * idToJITEntries[id] / idToEntries[id]).toFixed(2);
var interpEntriesPercent = 100 - (speshEntriesPercent + jitEntriesPercent);
var entry = {
Name: nodeIdToName[id],
Line: nodeIdToLine[id],
File: nodeIdToFile[id],
Entries: idToEntries[id],
InterpEntriesPercent: interpEntriesPercent,
SpeshEntriesPercent: speshEntriesPercent,
JITEntriesPercent: jitEntriesPercent,
InclusiveTime: +(idToInclusive[id] / 1000).toFixed(2),
InclusiveTimePercent: +(100 * idToInclusive[id] / totalInclusive).toFixed(2),
ExclusiveTime: +(idToExclusive[id] / 1000).toFixed(2),
ExclusiveTimePercent: +(100 * idToExclusive[id] / totalExclusive).toFixed(2),
OSR: idToOSR[id]
};
routineList.push(entry);
}
$scope.Routines = routineList;
$scope.predicate = "InclusiveTimePercent";
$scope.reverse = true;
});
moarProfApp.controller('CallGraphController', function ($scope) {
$scope.Current = rawData[0].call_graph;
$scope.total_time = rawData[0].total_time;
$scope.SuchCallers = false;
$scope.RecentCallers = [];
$scope.predicate = "TimePercent";
$scope.reverse = true;
var all_callers = [];
updateCurrentData();
$scope.toCallee = function (callee) {
// Update caller history.
all_callers.push($scope.Current);
$scope.RecentCallers.push($scope.Current);
if ($scope.RecentCallers.length > 5)
$scope.RecentCallers.shift();
// Update current node and callees.
$scope.Current = callee;
updateCurrentData();
};
$scope.toCaller = function (caller) {
// Update caller history.
while (all_callers.length > 0) {
var removed = all_callers.pop();
if ($scope.RecentCallers.length > 0)
$scope.RecentCallers.pop();
if (removed == caller)
break;
}
if (all_callers.length > $scope.RecentCallers.length) {
var ptr = all_callers.length - $scope.RecentCallers.length;
while (ptr >= 0 && $scope.RecentCallers.length < 5) {
$scope.RecentCallers.unshift(all_callers[ptr]);
ptr--;
}
}
// Update current node and callees.
$scope.Current = caller;
updateCurrentData();
}
/*
* Given a callee, create a unique, repeatable color;
* h/t https://stackoverflow.com/questions/3426404
*/
$scope.backgroundColor = function (callee) {
var str = callee.$$hashKey + callee.file + callee.name;
for (var i = 0, hash = 0; i < str.length; hash = str.charCodeAt(i++) + ((hash << 5) - hash));
for (var i = 0, colour = "#"; i < 3; colour += ("00" + ((hash >> i++ * 8) & 0xFF).toString(16)).slice(-2));
return colour;
}
function updateCurrentData() {
// Line and file.
var current = $scope.Current;
$scope.Line = current.line;
$scope.File = current.file;
// Entry statistics.
var interpEntries = current.entries - (current.spesh_entries + current.jit_entries);
var nonInlineEntries = current.entries - current.inlined_entries;
$scope.Entries = nonInlineEntries;
$scope.Percent = (100 * nonInlineEntries / current.entries).toFixed(2);
$scope.InlineEntries = current.inlined_entries;
$scope.InlinePercent = (100 * current.inlined_entries / current.entries).toFixed(2);
$scope.InterpEntries = interpEntries;
$scope.InterpPercent = (100 * interpEntries / current.entries).toFixed(2);
$scope.SpeshEntries = current.spesh_entries;
$scope.SpeshPercent = (100 * current.spesh_entries / current.entries).toFixed(2);
$scope.JITEntries = current.jit_entries;
$scope.JITPercent = (100 * current.jit_entries / current.entries).toFixed(2);
// Callees.
$scope.Callees = calleesOf(current);
}
function calleesOf(node) {
if (!node.callees)
return [];
var totalExclusive = 0.0;
node.callees.map(function (c) { totalExclusive += c.exclusive_time; });
return node.callees.map(function (c) {
var speshCallsPercent = +(100 * c.spesh_entries / c.entries).toFixed(2);
var jitCallsPercent = +(100 * c.jit_entries / c.entries).toFixed(2);
var interpCallsPercent = 100 - (speshCallsPercent + jitCallsPercent);
var inlinedPercent = +(100 * c.inlined_entries / c.entries).toFixed(2);
return {
Name: nodeIdToName[c.id],
Line: nodeIdToLine[c.id],
File: nodeIdToFile[c.id],
Calls: c.entries,
Time: +(c.inclusive_time / 1000).toFixed(2),
TimePercent: +(100 * c.inclusive_time / node.inclusive_time).toFixed(2),
InterpCallsPercent: interpCallsPercent,
SpeshCallsPercent: speshCallsPercent,
JITCallsPercent: jitCallsPercent,
InlinedPercent: inlinedPercent,
VeryInline: inlinedPercent >= 95,
SometimesInline: inlinedPercent < 95 && inlinedPercent > 10,
Node: c
};
});
}
});
moarProfApp.controller('AllocationsController', function ($scope, $modal) {
// Traverse all call nodes, counting up the allocations.
var typeIdToName = {};
var typeIdToAllocations = {};
var typeIdToAllocationsByType = {};
var typeIdToRoutineStats = {};
var maxAllocations = 1;
function walkCallGraphNode(node) {
if (node.allocations) {
node.allocations.map(function (alloc) {
alloc.type = id_to_things[alloc.id];
if (!typeIdToName[alloc.id]) {
typeIdToName[alloc.id] = alloc.type == "" ? "<anon>" : alloc.type;
typeIdToAllocations[alloc.id] = 0;
typeIdToAllocationsByType[alloc.id] = [0, 0, 0];
typeIdToRoutineStats[alloc.id] = {};
}
typeIdToAllocations[alloc.id] += alloc.count;
typeIdToAllocationsByType[alloc.id][0] += alloc.count - (alloc.spesh || 0) - (alloc.jit || 0);
typeIdToAllocationsByType[alloc.id][1] += alloc.spesh || 0;
typeIdToAllocationsByType[alloc.id][2] += alloc.jit || 0;
if (typeIdToAllocations[alloc.id] > maxAllocations)
maxAllocations = typeIdToAllocations[alloc.id];
if (typeIdToRoutineStats[alloc.id][node.id]) {
typeIdToRoutineStats[alloc.id][node.id]['count'] += alloc.count || 0;
typeIdToRoutineStats[alloc.id][node.id]['spesh'] += alloc.spesh || 0;
typeIdToRoutineStats[alloc.id][node.id]['jit'] += alloc.jit || 0;
} else {
typeIdToRoutineStats[alloc.id][node.id] = {
count: alloc.count || 0,
spesh: alloc.spesh || 0,
jit: alloc.jit || 0
};
}
});
}
if (node.callees) {
node.callees.map(walkCallGraphNode);
}
}
walkCallGraphNode(rawData[0].call_graph);
// Build allocation summary.
var allocationSummary = [];
for (id in typeIdToName) {
var maxAllocationByRoutine = 1;
for (var rid in typeIdToRoutineStats[id])
if (typeIdToRoutineStats[id][rid]['count'] > maxAllocationByRoutine)
maxAllocationByRoutine = typeIdToRoutineStats[id][rid]['count'];
var routineStats = [];
for (var rid in typeIdToRoutineStats[id])
routineStats.push({
Name: nodeIdToName[rid],
Line: nodeIdToLine[rid],
File: nodeIdToFile[rid],
Allocations: typeIdToRoutineStats[id][rid]['count'],
AllocationsSpesh: typeIdToRoutineStats[id][rid]['spesh'],
AllocationsJit: typeIdToRoutineStats[id][rid]['jit'],
AllocationsPercent: (100 * typeIdToRoutineStats[id][rid]['count'] / maxAllocationByRoutine),
AllocationsInterpPercent: (100 * (typeIdToRoutineStats[id][rid]['count'] - typeIdToRoutineStats[id][rid]['jit'] - typeIdToRoutineStats[id][rid]['spesh']) / maxAllocationByRoutine),
AllocationsSpeshPercent: (100 * typeIdToRoutineStats[id][rid]['spesh'] / maxAllocationByRoutine),
AllocationsJitPercent: (100 * typeIdToRoutineStats[id][rid]['jit'] / maxAllocationByRoutine)
});
var entry = {
Name: typeIdToName[id],
Allocations: typeIdToAllocations[id],
AllocationsPercent: +(100 * typeIdToAllocations[id] / maxAllocations).toFixed(2),
AllocationsInterpPercent: +(100 * typeIdToAllocationsByType[id][0] / maxAllocations).toFixed(2),
AllocationsSpeshPercent: +(100 * typeIdToAllocationsByType[id][1] / maxAllocations).toFixed(2),
AllocationsJitPercent: +(100 * typeIdToAllocationsByType[id][2] / maxAllocations).toFixed(2),
RoutineStats: routineStats
};
allocationSummary.push(entry);
}
$scope.AllocationSummary = allocationSummary;
$scope.predicate = "Allocations";
$scope.reverse = true;
$scope.routinePredicate = "Allocations";
$scope.routineReverse = true;
// Allocating routines handlng.
$scope.showAllocatingRoutines = function (alloc) {
// Show modal dialog with data.
$scope.CurrentAllocatingRoutine = alloc.Name;
$scope.CurrentAllocatingRoutineStats = alloc.RoutineStats;
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
scope: $scope
});
}
});
moarProfApp.controller('GCController', function ($scope) {
// Find longest GC run.
var longestGC = 0;
rawData[0].gcs.map(function (gc) {
if (gc.time > longestGC)
longestGC = gc.time;
});
// Produce something nice to render.
var run = 0;
$scope.GCs = rawData[0].gcs.map(function (gc) {
var totalBytes = gc.cleared_bytes + gc.retained_bytes + gc.promoted_bytes;
return {
Run: ++run,
Time: +(gc.time / 1000).toFixed(2),
Full: (gc.full != 0),
TimePercent: +(100 * gc.time / longestGC).toFixed(2),
RetainedKilobytes: Math.round(gc.retained_bytes / 1024),
PromotedKilobytes: Math.round(gc.promoted_bytes / 1024),
ClearedKilobytes: Math.round(gc.cleared_bytes / 1024),
RetainedPercent: +(100 * gc.retained_bytes / totalBytes).toFixed(2),
PromotedPercent: +(100 * gc.promoted_bytes / totalBytes).toFixed(2),
ClearedPercent: +(100 * gc.cleared_bytes / totalBytes).toFixed(2),
Gen2Roots: 'gen2_roots' in gc ? gc.gen2_roots : 0
};
});
$scope.predicate = 'Run';
$scope.reverse = false;
});
moarProfApp.controller('OSRDeoptController', function ($scope) {
// Walk call graph to build data.
var idToOSR = {};
var idToDeoptOne = {};
var idToDeoptAll = {};
var maxOSR = 1;
var maxDeoptOne = 1;
var maxDeoptAll = 1;
function walkCallGraphNode(node) {
if (!idToOSR[node.id]) {
idToOSR[node.id] = 0;
idToDeoptOne[node.id] = 0;
idToDeoptAll[node.id] = 0;
}
idToOSR[node.id] += node.osr || 0;
idToDeoptOne[node.id] += node.deopt_one || 0;
idToDeoptAll[node.id] += node.deopt_all || 0;
if (idToOSR[node.id] > maxOSR)
maxOSR = idToOSR[node.id];
if (idToDeoptOne[node.id] > maxDeoptOne)
maxDeoptOne = idToDeoptOne[node.id];
if (idToDeoptAll[node.id] > maxDeoptAll)
maxDeoptAll = idToDeoptAll[node.id];
if (node.callees)
node.callees.map(walkCallGraphNode);
}
walkCallGraphNode(rawData[0].call_graph);
// Build up OSR, deopt one, and deopt all tables.
var osrs = [];
var deoptOnes = [];
var deoptAlls = [];
for (id in idToOSR) {
if (idToOSR[id] > 0) {
osrs.push({
Name: nodeIdToName[id],
Line: nodeIdToLine[id],
File: nodeIdToFile[id],
Count: idToOSR[id],
Percent: Math.round(100 * idToOSR[id] / maxOSR)
});
}
if (idToDeoptOne[id] > 0) {
deoptOnes.push({
Name: nodeIdToName[id],
Line: nodeIdToLine[id],
File: nodeIdToFile[id],
Count: idToDeoptOne[id],
Percent: Math.round(100 * idToDeoptOne[id] / maxDeoptOne)
});
}
if (idToDeoptAll[id] > 0) {
deoptAlls.push({
Name: nodeIdToName[id],
Line: nodeIdToLine[id],
File: nodeIdToFile[id],
Count: idToDeoptAll[id],
Percent: Math.round(100 * idToDeoptAll[id] / maxDeoptAll)
});
}
}
$scope.OSRs = osrs;
$scope.DeoptOnes = deoptOnes;
$scope.DeoptAlls = deoptAlls;
$scope.predicate = 'Count';
$scope.reverse = true;
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en" ng-app="moarProfApp" ng-controller="NavigationController">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MoarVM Profiler Results</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css">
<style>
body {
min-height: 500px;
padding-top: 70px;
}
.icyclegraph .call {
display: block;
border: 1px solid black;
overflow: hidden;
}
.icyclegraph .child {
float: left;
}
.icyclegraph a {
color: black;
}
</style>
</head>
<body >
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand">MoarVM Profiler Results</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li ng-class="Tab == 'Overview' ? 'active' : ''"><a href="#overview" ng-click="Tab = 'Overview'">Overview</a></li>
<li ng-class="Tab == 'Routines' ? 'active' : ''"><a href="#routines" ng-click="Tab = 'Routines'">Routines</a></li>
<li ng-class="Tab == 'Call Graph' ? 'active' : ''"><a href="#callgraph" ng-click="Tab = 'Call Graph'">Call Graph</a></li>
<li ng-class="Tab == 'Allocations' ? 'active' : ''"><a href="#allocations" ng-click="Tab = 'Allocations'">Allocations</a></li>
<li ng-class="Tab == 'GC' ? 'active' : ''"><a href="#gc" ng-click="Tab = 'GC'">GC</a></li>
<li ng-class="Tab == 'OSR/Deopt' ? 'active' : ''"><a href="#osrdeopt" ng-click="Tab = 'OSR/Deopt'">OSR / Deopt</a></li>
</ul>
</div>
</div>
</div>
<div class="container" ng-show="Tab == 'Overview'">
<div ng-controller="OverviewController">
<h3>Time Spent</h3>
<p>The profiled code ran for <strong>{{TotalTime}}ms</strong>. Of this,
<strong>{{OverheadTime}}ms</strong> were spent on garbage collection
and dynamic optimization (that's <strong>{{OverheadTimePercent}}%</strong>).
</p>
<table class="table table-striped table-condensed table-bordered">
<tbody>
<tr>
<td><strong>Executing Code</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{ExecutingTimePercent}}%;">
</div>
</div>
</div>
<div>
{{ExecutingTimePercent}}%
({{ExecutingTime}}ms)
</div>
</td>
</tr>
<tr>
<td><strong>Garbage Collection</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{GCTimePercent}}%;">
</div>
</div>
</div>
<div>
{{GCTimePercent}}%
({{GCTime}}ms)
</div>
</td>
</tr>
<tr>
<td><strong>Dynamic Optimization</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{SpeshTimePercent}}%;">
</div>
</div>
</div>
<div>
{{SpeshTimePercent}}%
({{SpeshTime}}ms)
</div>
</td>
</tr>
</tbody>
</table>
<h3>Call Frames</h3>
<p>In total, <strong>{{EntriesWithoutInline}} call frames</strong> were
entered and exited by the profiled code. Inlining eliminated the need
to create <strong>{{EntriesInline}} call frames</strong> (that's
<strong>{{InlinePercent}}%</strong>).
</p>
<table class="table table-striped table-condensed table-bordered">
<tbody>
<tr>
<td><strong>Interpreted Frames</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{InterpFramesPercent}}%;">
</div>
</div>
</div>
<div>
{{InterpFramesPercent}}%
({{InterpFrames}})
</div>
</td>
</tr>
<tr>
<td><strong>Specialized Frames</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{SpeshFramesPercent}}%;">
</div>
</div>
</div>
<div>
{{SpeshFramesPercent}}%
({{SpeshFrames}})
</div>
</td>
</tr>
<tr>
<td><strong>JIT-Compiled Frames</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{JITFramesPercent}}%;">
</div>
</div>
</div>
<div>
{{JITFramesPercent}}%
({{JITFrames}})
</div>
</td>
</tr>
</tbody>
</table>
<h3>Garbage Collection</h3>
<p>The profiled code did <strong>{{GCRuns}} garbage collections</strong>.
There were <strong>{{FullGCRuns}} full collections</strong> involving
the entire heap.
</p>
<p ng-show="{{GCRuns > 0}}">
The average nursery collection time was <strong>{{NurseryAverage}}ms</strong>.
<span ng-show="{{FullGCRuns > 0}}">
The average full collection time was <strong>{{FullAverage}}ms</strong>.
</span>
</p>
<h3>Dynamic Optimization</h3>
<p>Of {{OptimizedFrames}} specialized or JIT-compiled frames, there were
<strong>{{DeoptOnes}} deoptimizations</strong> (that's <strong>
{{DeoptOnePercent}}%</strong> of all optimized frames).
</p>
<p ng-show="DeoptAlls == 0">
There was <strong>no global deoptimization</strong> triggered by the
profiled code.
</p>
<p ng-show="DeoptAlls == 1">
There was <strong>one global deoptimization</strong> triggered by the
profiled code.
</p>
<p ng-show="DeoptAlls > 1">
There were <strong>{{DeoptAlls}} global deoptimization</strong> triggered
by the profiled code.
</p>
<p ng-show="OSRs == 0">
There was <strong>no On Stack Replacement</strong> performed while
executing the profiled code (normal if the code lacks long-running
loops with many iterations).
</p>
<p ng-show="OSRs == 1">
There was <strong>one On Stack Replacement</strong> performed while
executing the profiled code.
</p>
<p ng-show="OSRs > 1">
There were <strong>{{OSRs}} On Stack Replacements</strong> performed
while executing the profiled code.
</p>
</div>
</div>
<div class="container" ng-show="Tab == 'Routines'">
<div ng-controller="RoutinesController">
<table class="table table-striped table-condensed table-bordered">
<thead>
<th>
<a href="" ng-click="reverse = predicate == 'Name' ? !reverse : false; predicate = 'Name';">Name</a>
<input ng-model="NameFilter">
</th>
<th><a href="" ng-click="reverse = predicate == 'Entries' ? !reverse : true; predicate = 'Entries';">Entries</a></th>
<th><a href="" ng-click="reverse = predicate == 'InclusiveTimePercent' ? !reverse : true; predicate = 'InclusiveTimePercent';">Inclusive Time</a></th>
<th><a href="" ng-click="reverse = predicate == 'ExclusiveTimePercent' ? !reverse : true; predicate = 'ExclusiveTimePercent';">Exclusive Time</a></th>
<th>
<span class="text-danger" tooltip-placement="bottom" tooltip="Unoptimized interpreted code">Interp</span> /
<span class="text-warning" tooltip-placement="bottom" tooltip="Type-specialized interpreted code">Spesh</span> /
<span class="text-success" tooltip-placement="bottom" tooltip="Type-specialized, JIT-compiled code">JIT</span>
</th>
</thead>
<tbody>
<tr ng-repeat="routine in Routines | filter:NameFilter | orderBy:predicate:reverse">
<td>
<strong>{{routine.Name}}</strong><br />
<span class="text-muted">{{routine.File}}:{{routine.Line}}</span>
</td>
<td>{{routine.Entries}}</td>
<td>
<div class="pull-left" style="width: 70px">
<div class="progress" style="width: 60px">
<div class="progress-bar" role="progressbar" style="width: {{routine.InclusiveTimePercent}}%;">
</div>
</div>
</div>
<div>
<strong>{{routine.InclusiveTimePercent}}%</strong>
({{routine.InclusiveTime}}ms)
</div>
</td>
<td>
<div class="pull-left" style="width: 70px">
<div class="progress" style="width: 60px">
<div class="progress-bar" role="progressbar" style="width: {{routine.ExclusiveTimePercent}}%;">
</div>
</div>
</div>
<div>
<strong>{{routine.ExclusiveTimePercent}}%</strong>
({{routine.ExclusiveTime}}ms)
</div>
</td>
<td>
<div class="pull-left" style="width: 110px">
<div class="progress" style="width: 100px">
<div class="progress-bar progress-bar-danger" style="width: {{routine.InterpEntriesPercent}}%">
</div>
<div class="progress-bar progress-bar-warning" style="width: {{routine.SpeshEntriesPercent}}%">
</div>
<div class="progress-bar progress-bar-success" style="width: {{routine.JITEntriesPercent}}%">
</div>
</div>
</div>
<div>
<span class="label label-default" ng-show="routine.OSR">OSR</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<script type="text/ng-template" id="icycle_graph_callee_renderer.html">
<a href="#" class="call" style="background-color:{{backgroundColor(callee)}}" ng-click="toCallee(callee)">{{callee.name}}</a>
<div class="child" style="width: {{callee.inclusive_time * 100 / Current.inclusive_time}}%;" ng-repeat="callee in callee.callees" title="{{callee.name}}" ng-include="'icycle_graph_callee_renderer.html'"></div>
</script>
<div class="container" ng-show="Tab == 'Call Graph'">
<div ng-controller="CallGraphController">
<ol class="breadcrumb">
<li><strong>Callers:</strong></li>
<li ng-show="RecentCallers.length == 0"><em>None</em></li>
<li ng-repeat="caller in RecentCallers">
<a href="" ng-click="toCaller(caller)">
{{caller.name == '' ? '&lt;anon&gt;' : caller.name}}
</a>
</li>
</ol>
<p>
<span class="lead">{{Current.name == '' ? '&lt;anon&gt;' : Current.name}}</span><br />
<span class="text-muted">{{File}}:{{Line}}</span>
</p>
<div class="icyclegraph">
<div class="child" ng-repeat="callee in [Current]" ng-include="'icycle_graph_callee_renderer.html'"></div>
</div>
<table class="table table-striped table-condensed table-bordered">
<tbody>
<tr>
<td><strong>Calls <span class="text-success">(Inlined)</span></strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar" role="progressbar" style="width: {{Percent}}%;">
</div>
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{InlinePercent}}%;">
</div>
</div>
</div>
<div ng-show="InlinePercent == 0">
{{Entries}}
</div>
<div ng-show="InlinePercent != 0">
{{Entries}} +
<span class="text-success">{{InlineEntries}} ({{InlinePercent}}%)</span>
</div>
</td>
</tr>
<tr>
<td><strong>Interpreted Calls</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{InterpPercent}}%;">
</div>
</div>
</div>
<div>
{{InterpPercent}}%
({{InterpEntries}})
</div>
</td>
</tr>
<tr>
<td><strong>Specialized Calls</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{SpeshPercent}}%;">
</div>
</div>
</div>
<div>
{{SpeshPercent}}%
({{SpeshEntries}})
</div>
</td>
</tr>
<tr>
<td><strong>JIT-Compiled Calls</strong></td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{JITPercent}}%;">
</div>
</div>
</div>
<div>
{{JITPercent}}%
({{JITEntries}})
</div>
</td>
</tr>
</tbody>
</table>
<div class="panel panel-default">
<div class="panel-heading">Callees</div>
<table class="table table-striped table-condensed table-bordered" ng-show="Callees.length > 0">
<thead>
<th>
<a href="" ng-click="reverse = predicate == 'Name' ? !reverse : false; predicate = 'Name';">Name</a>
<input ng-model="NameFilter">
</th>
<th><a href="" ng-click="reverse = predicate == 'Calls' ? !reverse : true; predicate = 'Calls';">Calls</a></th>
<th><a href="" ng-click="reverse = predicate == 'TimePercent' ? !reverse : true; predicate = 'TimePercent';">Time In Callee</a></th>
<th>
<span class="text-danger" tooltip-placement="bottom" tooltip="Unoptimized interpreted code">Interp</span> /
<span class="text-warning" tooltip-placement="bottom" tooltip="Type-specialized interpreted code">Spesh</span> /
<span class="text-success" tooltip-placement="bottom" tooltip="Type-specialized, JIT-compiled code">JIT</span>
</th>
<th>
<span tooltip-placement="bottom" tooltip="Code from this callee was flattened into the routine by the optimizer">Inlined</span>
</th>
</thead>
<tbody>
<tr ng-repeat="callee in Callees | filter:NameFilter | orderBy:predicate:reverse">
<td>
<strong><a href="" ng-click="toCallee(callee.Node)">{{callee.Name}}</a></strong><br />
<span class="text-muted">{{callee.File}}:{{callee.Line}}</span>
</td>
<td>{{callee.Calls}}</td>
<td>
<div class="pull-left" style="width: 70px">
<div class="progress" style="width: 60px">
<div class="progress-bar" role="progressbar" style="width: {{callee.TimePercent}}%;">
</div>
</div>
</div>
<div>
<strong>{{callee.TimePercent}}%</strong>
({{callee.Time}}ms)
</div>
</td>
<td>
<div class="progress" style="width: 100px">
<div class="progress-bar progress-bar-danger" style="width: {{callee.InterpCallsPercent}}%">
</div>
<div class="progress-bar progress-bar-warning" style="width: {{callee.SpeshCallsPercent}}%">
</div>
<div class="progress-bar progress-bar-success" style="width: {{callee.JITCallsPercent}}%">
</div>
</div>
</td>
<td>
<div ng-show="callee.VeryInline">
<span class="text-success">
<span class="glyphicon glyphicon-star"></span>
</span>
{{callee.InlinedPercent}}%
</div>
<div ng-show="callee.SometimesInline">
<span class="text-muted">
<span class="glyphicon glyphicon-star-empty"></span>
</span>
{{callee.InlinedPercent}}%
</div>
</td>
</tr>
</tbody>
</table>
<div class="panel-body" ng-show="Callees.length == 0">
This code has no callees.
</div>
</div>
</div>
</div>
<div class="container" ng-show="Tab == 'Allocations'">
<div ng-controller="AllocationsController">
<table class="table table-striped table-condensed table-bordered">
<thead>
<th>
<a href="" ng-click="reverse = predicate == 'Name' ? !reverse : false; predicate = 'Name';">Name</a>
<input ng-model="NameFilter">
</th>
<th><a href="" ng-click="reverse = predicate == 'Allocations' ? !reverse : true; predicate = 'Allocations';">Allocations</a></th>
<th>Allocating Routines</th>
</thead>
<tbody>
<tr ng-repeat="alloc in AllocationSummary | filter:NameFilter | orderBy:predicate:reverse">
<td><strong>{{alloc.Name}}</strong></td>
<td>
<div class="pull-left" style="width: 210px">
<div class="progress" style="width: 200px">
<div class="progress-bar progress-bar-danger" role="progressbar" style="width: {{alloc.AllocationsInterpPercent}}%;">
</div>
<div class="progress-bar progress-bar-warning" role="progressbar" style="width: {{alloc.AllocationsSpeshPercent}}%;">
</div>
<div class="progress-bar progress-bar-success" role="progressbar" style="width: {{alloc.AllocationsJitPercent}}%;">
</div>
</div>
</div>
<div>
{{alloc.Allocations}}
</div>
</td>
<td>
<a href="" ng-click="showAllocatingRoutines(alloc)">View</a>
</td>
</tr>
</tbody>
</table>
<script type="text/ng-template" id="myModalContent.html">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
<h4 class="modal-title" id="allocatingRoutinesModalLabel">
Routines Allocating {{CurrentAllocatingRoutine}}
</h4>
</div>
<div class="modal-body">
<table class="table table-striped table-condensed table-bordered">
<thead>
<th>
<a href="" ng-click="routineReverse = routinePredicate == 'Name' ? !routineReverse : false; routinePredicate = 'Name';">Name</a>
<input ng-model="RoutineNameFilter">
</th>
<th><a href="" ng-click="routineReverse = routinePredicate == 'Allocations' ? !routineReverse : true; routinePredicate = 'Allocations';">Allocations</a></th>
</thead>
<tbody>
<tr ng-repeat="routine in CurrentAllocatingRoutineStats | filter:RoutineNameFilter | orderBy:routinePredicate:routineReverse">
<td>
<strong>{{routine.Name}}</strong><br />
<span class="text-muted">{{routine.File}}:{{routine.Line}}</span>
</td>
<td>
<div class="pull-left" style="width: 210px">
<div class="progress" style="width: 200px">
<div class="progress-bar progress-bar-danger" style="width: {{routine.AllocationsInterpPercent}}%;">
</div>
<div class="progress-bar progress-bar-warning" style="width: {{routine.AllocationsSpeshPercent}}%;">
</div>
<div class="progress-bar progress-bar-success" style="width: {{routine.AllocationsJitPercent}}%;">
</div>
</div>
</div>
<div>
{{routine.Allocations}}
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</script>
</div>
</div>
<div class="container" ng-show="Tab == 'GC'">
<div ng-controller="GCController">
<table class="table table-striped table-condensed table-bordered">
<thead>
<th>
<a href="" ng-click="reverse = predicate == 'Run' ? !reverse : false; predicate = 'Run';">Run</a>
</th>
<th>Full</th>
<th>
<a href="" ng-click="reverse = predicate == 'Time' ? !reverse : false; predicate = 'Time';">Time</a>
</th>
<th>
Nursery:
<span class="text-danger" tooltip-placement="bottom" tooltip="Bytes retained in the nursery">Retained</span> /
<span class="text-warning" tooltip-placement="bottom" tooltip="Bytes promoted to gen2 and now available in nursery">Promoted</span> /
<span class="text-success" tooltip-placement="bottom" tooltip="Bytes released and now availabe in nursery">Freed</span>
</th>
</thead>
<tbody>
<tr ng-repeat="gc in GCs | orderBy:predicate:reverse">
<td><strong>{{gc.Run}}</strong></td>
<td>
<span class="text-success" ng-show="gc.Full">
<span class="glyphicon glyphicon-star"></span>
</span>
</td>
<td>
<div class="pull-left" style="width: 210px">
<div class="progress" style="width: 200px">
<div class="progress-bar" role="progressbar" style="width: {{gc.TimePercent}}%;">
</div>
</div>
</div>
<div>
{{gc.Time}}ms
</div>
</td>
<td>
<div class="pull-left" style="width: 210px">
<div class="progress" style="width: 200px">
<div class="progress-bar progress-bar-danger" style="width: {{gc.RetainedPercent}}%">
</div>
<div class="progress-bar progress-bar-warning" style="width: {{gc.PromotedPercent}}%">
</div>
<div class="progress-bar progress-bar-success" style="width: {{gc.ClearedPercent}}%">
</div>
</div>
</div>
<div>
{{gc.RetainedKilobytes}}KB /
{{gc.PromotedKilobytes}}KB /
{{gc.ClearedKilobytes}}KB ...
<small>{{gc.Gen2Roots}} gen2 roots</small>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="container" ng-show="Tab == 'OSR/Deopt'">
<div ng-controller="OSRDeoptController">
<h3>OSR</h3>
<p>On Stack Replacement detects routines containing hot loops that are
being interpreted, and replaces them with specialized or JIT-compiled
code.</p>
<table class="table table-striped table-condensed table-bordered" ng-show="OSRs.length > 0">
<thead>
<th>
Routine
</th>
<th>On Stack Replacements</th>
</thead>
<tbody>
<tr ng-repeat="osr in OSRs | orderBy:predicate:reverse">
<td>
<strong>{{osr.Name}}</strong><br />
<span class="text-muted">{{osr.File}}:{{osr.Line}}</span>
</td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar" role="progressbar" style="width: {{osr.Percent}}%;">
</div>
</div>
</div>
<div>
{{osr.Count}}
</div>
</td>
</tr>
</tbody>
</table>
<p ng-show="OSRs.length == 0">
<em>No OSR was performed during this profile.</em>
</p>
<h3>Local Deoptimization</h3>
<p>Local deoptimization happens when a guard in specialized or JIT-compiled
code fails. Since the code was produced assuming the guard would hold,
the VM falls back to running the safe, but slower, interpreted code.</p>
<table class="table table-striped table-condensed table-bordered" ng-show="DeoptOnes.length > 0">
<thead>
<th>
Routine
</th>
<th>Deoptimizations</th>
</thead>
<tbody>
<tr ng-repeat="deopt in DeoptOnes | orderBy:predicate:reverse">
<td>
<strong>{{deopt.Name}}</strong><br />
<span class="text-muted">{{deopt.File}}:{{deopt.Line}}</span>
</td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar" role="progressbar" style="width: {{deopt.Percent}}%;">
</div>
</div>
</div>
<div>
{{deopt.Count}}
</div>
</td>
</tr>
</tbody>
</table>
<p ng-show="DeoptOnes.length == 0">
<em>No local deoptimizations occurred during this profile.</em>
</p>
<h3>Global Deoptimization</h3>
<p>Global deoptimization happens when an event occurs that renders
all currently type-specialized or JIT-compiled code on the call
stack potentially invalid. Mixins - changing the type of an object
in place - are a common reason.</p>
<table class="table table-striped table-condensed table-bordered" ng-show="DeoptAlls.length > 0">
<thead>
<th>
Routine
</th>
<th>Deoptimizations</th>
</thead>
<tbody>
<tr ng-repeat="deopt in DeoptAlls | orderBy:predicate:reverse">
<td>
<strong>{{deopt.Name}}</strong><br />
<span class="text-muted">{{deopt.File}}:{{deopt.Line}}</span>
</td>
<td>
<div class="pull-left" style="width: 310px">
<div class="progress" style="width: 300px">
<div class="progress-bar" role="progressbar" style="width: {{deopt.Percent}}%;">
</div>
</div>
</div>
<div>
{{deopt.Count}}
</div>
</td>
</tr>
</tbody>
</table>
<p ng-show="DeoptAlls.length == 0">
<em>No global deoptimizations occurred during this profile.</em>
</p>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.7/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.2.0/ui-bootstrap-tpls.js"></script>
<script>
var rawData = JSON.parse('[{"9":"List","8":{"name":"","line":3152,"file":"gen/moar/BOOTSTRAP.nqp"},"7":"CallCapture","6":"BOOTHash","57":{"name":"sink","line":13,"file":"SETTING::src/core/Mu.pm"},"56":{"name":"","line":-1,"file":"/Users/liz/Github/rakudo.moar/install/share/perl6/runtime/CORE.setting.moarvm"},"55":{"name":"last","line":77,"file":"SETTING::src/core/control.pm"},"54":{"name":"last","line":76,"file":"SETTING::src/core/control.pm"},"53":{"name":"","line":1,"file":"-e"},"52":"Int","51":{"name":"floor","line":135,"file":"SETTING::src/core/Num.pm"},"50":{"name":"rand","line":126,"file":"SETTING::src/core/Num.pm"},"5":{"name":"roll","line":1079,"file":"SETTING::src/core/List.pm"},"49":"Num","48":{"name":"Bridge","line":54,"file":"SETTING::src/core/Int.pm"},"47":{"name":"rand","line":9,"file":"SETTING::src/core/Real.pm"},"46":{"name":"","line":1093,"file":"SETTING::src/core/List.pm"},"45":{"name":"pull-one","line":962,"file":"SETTING::src/core/Rakudo/Internals.pm"},"44":{"name":"","line":448,"file":"SETTING::src/core/Any-iterable-methods.pm"},"43":{"name":"sink-all","line":440,"file":"SETTING::src/core/Any-iterable-methods.pm"},"42":{"name":"sink","line":158,"file":"SETTING::src/core/Seq.pm"},"41":"Any::IterateOneWithoutPhasers","40":{"name":"new","line":334,"file":"SETTING::src/core/Any-iterable-methods.pm"},"4":{"name":"<unit>","line":1,"file":"-e"},"39":{"name":"","line":1437,"file":"gen/moar/BOOTSTRAP.nqp"},"38":{"name":"returns","line":7,"file":"SETTING::src/core/Block.pm"},"37":{"name":"infix:«<»","line":338,"file":"SETTING::src/core/Int.pm"},"36":{"name":"has-phasers","line":36,"file":"SETTING::src/core/Block.pm"},"35":{"name":"count","line":13,"file":"SETTING::src/core/Code.pm"},"34":{"name":"sequential-map","line":779,"file":"SETTING::src/core/Any-iterable-methods.pm"},"33":{"name":"type_check","line":2659,"file":"gen/moar/Metamodel.nqp"},"32":{"name":"iterator","line":52,"file":"SETTING::src/core/Seq.pm"},"31":{"name":"map","line":17,"file":"SETTING::src/core/Any-iterable-methods.pm"},"30":{"name":"map","line":11,"file":"SETTING::src/core/Any-iterable-methods.pm"},"3":"BOOTArray","29":"Seq","28":{"name":"new","line":42,"file":"SETTING::src/core/Seq.pm"},"27":"<anon|140492422530512>","26":{"name":"new","line":959,"file":"SETTING::src/core/Rakudo/Internals.pm"},"25":{"name":"","line":957,"file":"SETTING::src/core/Rakudo/Internals.pm"},"24":{"name":"UnendingCallableIterator","line":956,"file":"SETTING::src/core/Rakudo/Internals.pm"},"23":"Block","22":{"name":"","line":1660,"file":"gen/moar/BOOTSTRAP.nqp"},"21":{"name":"elems","line":536,"file":"SETTING::src/core/List.pm"},"20":{"name":"is-lazy","line":1001,"file":"SETTING::src/core/List.pm"},"2":"BOOTCode","19":"Scalar","18":{"name":"roll","line":1087,"file":"SETTING::src/core/List.pm"},"17":{"name":"add_to_cache","line":2451,"file":"gen/moar/BOOTSTRAP.nqp"},"16":{"name":"is_narrower","line":1853,"file":"gen/moar/BOOTSTRAP.nqp"},"15":"BOOTIntArray","14":"NQPArrayIter","13":{"name":"","line":1823,"file":"gen/moar/BOOTSTRAP.nqp"},"12":"BOOTInt","11":"NQPArray","10":{"name":"","line":2167,"file":"gen/moar/BOOTSTRAP.nqp"},"1":{"name":"<unit-outer>","line":1,"file":"-e"},"0":{"name":"","line":1570,"file":"gen/moar/stage2/NQPHLL.nqp"}},{"total_time":464179,"spesh_time":1182,"gcs":[{"time":10260,"retained_bytes":1040272,"promoted_bytes":0,"gen2_roots":12677,"full":0,"cleared_bytes":3154024},{"time":11064,"retained_bytes":40,"promoted_bytes":6137103,"gen2_roots":17186,"full":0,"cleared_bytes":4293024425},{"time":9444,"retained_bytes":40,"promoted_bytes":0,"gen2_roots":2068,"full":0,"cleared_bytes":4194240}],"call_graph":{"inclusive_time":431864,"id":"0","exclusive_time":6,"entries":1,"callees":[{"inclusive_time":431857,"id":"1","exclusive_time":19,"entries":1,"callees":[{"inclusive_time":431838,"id":"4","exclusive_time":32,"entries":1,"callees":[{"inclusive_time":501,"id":"5","exclusive_time":150,"entries":1,"callees":[{"inclusive_time":0,"id":"8","exclusive_time":0,"entries":1,"allocations":[{"id":"9","count":1}]},{"inclusive_time":182,"id":"10","exclusive_time":83,"entries":1,"callees":[{"inclusive_time":97,"id":"13","exclusive_time":91,"entries":1,"callees":[{"inclusive_time":6,"id":"16","exclusive_time":6,"entries":6}],"allocations":[{"id":"11","count":17},{"id":"14","count":4},{"id":"6","count":6},{"id":"15","count":6},{"id":"12","count":3}]},{"inclusive_time":0,"id":"17","exclusive_time":0,"entries":1}],"allocations":[{"id":"2","count":1},{"id":"11","count":2},{"id":"6","count":1},{"id":"12","count":1}]},{"inclusive_time":168,"id":"18","exclusive_time":80,"entries":1,"callees":[{"inclusive_time":1,"id":"20","exclusive_time":1,"entries":1,"allocations":[{"id":"6","count":1},{"id":"19","count":1}]},{"inclusive_time":0,"id":"21","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1}]},{"inclusive_time":1,"id":"22","exclusive_time":1,"entries":1,"allocations":[{"id":"23","count":1},{"id":"2","count":1}]},{"inclusive_time":83,"id":"24","exclusive_time":80,"entries":1,"callees":[{"inclusive_time":1,"id":"25","exclusive_time":1,"entries":1,"allocations":[{"id":"19","count":1}]},{"inclusive_time":1,"id":"26","exclusive_time":1,"entries":1,"allocations":[{"id":"6","count":1},{"id":"27","count":1}]}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":0,"id":"28","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1},{"id":"29","count":1}]}],"allocations":[{"id":"6","count":1},{"id":"19","count":1}]}],"allocations":[{"id":"6","count":1},{"id":"3","count":1},{"id":"7","count":3}]},{"inclusive_time":0,"id":"22","exclusive_time":0,"entries":1,"allocations":[{"id":"23","count":1},{"id":"2","count":1}]},{"inclusive_time":122,"id":"30","exclusive_time":16,"entries":1,"callees":[{"inclusive_time":0,"id":"8","exclusive_time":0,"entries":1,"allocations":[{"id":"9","count":1}]},{"inclusive_time":11,"id":"10","exclusive_time":10,"entries":1,"callees":[{"inclusive_time":0,"id":"17","exclusive_time":0,"entries":1}],"allocations":[{"id":"2","count":1},{"id":"11","count":2},{"id":"6","count":1},{"id":"12","count":1}]},{"inclusive_time":94,"id":"31","exclusive_time":39,"entries":1,"callees":[{"inclusive_time":11,"id":"32","exclusive_time":10,"entries":1,"callees":[{"inclusive_time":0,"id":"33","exclusive_time":0,"entries":1}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":43,"id":"34","exclusive_time":34,"entries":1,"callees":[{"inclusive_time":0,"id":"35","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1}]},{"inclusive_time":0,"id":"36","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1}]},{"inclusive_time":0,"id":"37","exclusive_time":0,"entries":1},{"inclusive_time":4,"id":"38","exclusive_time":4,"entries":1,"callees":[{"inclusive_time":0,"id":"39","exclusive_time":0,"entries":1}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":1,"id":"40","exclusive_time":1,"entries":1,"allocations":[{"id":"19","count":2},{"id":"6","count":1},{"id":"41","count":1}]},{"inclusive_time":0,"id":"28","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1},{"id":"29","count":1}]}],"allocations":[{"id":"19","count":2}]}],"allocations":[{"id":"19","count":2},{"id":"6","count":1}]}],"allocations":[{"id":"3","count":1},{"id":"7","count":3}]},{"inclusive_time":431181,"id":"42","exclusive_time":17,"entries":1,"callees":[{"inclusive_time":1,"id":"32","exclusive_time":1,"entries":1,"callees":[{"inclusive_time":0,"id":"33","exclusive_time":0,"entries":1}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":431161,"id":"43","exclusive_time":15,"entries":1,"callees":[{"inclusive_time":0,"id":"22","exclusive_time":0,"entries":1,"allocations":[{"id":"23","count":1},{"id":"2","count":1}]},{"osr":1,"jit_entries":1,"inclusive_time":431145,"id":"44","exclusive_time":41062,"entries":1,"callees":[{"jit_entries":99844,"inclusive_time":384006,"id":"45","exclusive_time":21074,"entries":100001,"callees":[{"jit_entries":99844,"inclusive_time":362931,"id":"46","exclusive_time":44566,"entries":100001,"callees":[{"spesh_entries":99844,"inclusive_time":60854,"id":"47","exclusive_time":38201,"entries":100001,"callees":[{"spesh_entries":99844,"inclusive_time":10547,"id":"48","exclusive_time":10547,"entries":100001,"allocations":[{"id":"6","count":157},{"spesh":99844,"id":"49","count":100001}]},{"spesh_entries":99844,"inclusive_time":12104,"id":"50","exclusive_time":12104,"entries":100001,"allocations":[{"id":"6","count":157},{"spesh":99844,"id":"49","count":100001}]}],"allocations":[{"id":"6","count":157}]},{"spesh_entries":99844,"inclusive_time":257511,"id":"51","exclusive_time":257511,"entries":100001,"allocations":[{"id":"6","count":157},{"spesh":99844,"id":"52","count":100001}]}],"allocations":[{"id":"19","count":1}]}],"allocations":[{"id":"6","count":157}]},{"jit_entries":99844,"inclusive_time":6077,"id":"53","exclusive_time":6036,"entries":100001,"callees":[{"inclusive_time":40,"id":"54","exclusive_time":13,"entries":1,"callees":[{"inclusive_time":12,"id":"10","exclusive_time":11,"entries":1,"callees":[{"inclusive_time":1,"id":"17","exclusive_time":1,"entries":1}],"allocations":[{"id":"2","count":1},{"id":"11","count":2},{"id":"6","count":1},{"id":"12","count":1}]},{"inclusive_time":14,"id":"55","exclusive_time":13,"entries":1,"callees":[{"inclusive_time":1,"id":"56","exclusive_time":1,"entries":1,"allocations":[{"id":"52","count":1}]}]}],"allocations":[{"id":"7","count":3}]}]}],"allocations":[{"id":"19","count":1}]}],"allocations":[{"id":"6","count":1}]},{"inclusive_time":0,"id":"57","exclusive_time":0,"entries":1,"allocations":[{"id":"6","count":1}]}],"allocations":[{"id":"6","count":1}]}]}],"allocations":[{"id":"2","count":1},{"id":"3","count":1}]}]}}]');
// grab the "dictionary" from the very first entry
var id_to_things = rawData[0];
// the rest of the program expects the list to contain callframes, not the dictionary
// so we just shift it off the beginning.
rawData.shift();
// Extract some common things out of the raw data.
var nodeIdToName = {};
var nodeIdToFile = {};
var nodeIdToLine = {};
(function () {
function walkCallGraphNode(node) {
if (!nodeIdToName[node.id]) {
var existing_data = id_to_things[node.id.toString()];
node.name = existing_data.name;
node.line = existing_data.line;
node.file = existing_data.file;
nodeIdToName[node.id] = node.name == "" ? "<anon>" : node.name;
nodeIdToLine[node.id] = node.line < 1 ? "<unknown>" : node.line;
nodeIdToFile[node.id] = node.file == "" ? "<unknown>" : node.file;
}
if (node.callees)
node.callees.map(walkCallGraphNode);
}
walkCallGraphNode(rawData[0].call_graph);
}());
// Register application and add controllers, increase recursion depth for callGraphController
var moarProfApp = angular.module('moarProfApp', ['ui.bootstrap']).config(function($rootScopeProvider) {
$rootScopeProvider.digestTtl(100);
});
moarProfApp.controller('NavigationController', function ($scope) {
$scope.Tab = 'Overview';
});
moarProfApp.controller('OverviewController', function ($scope) {
var totalTime = rawData[0].total_time;
var speshTime = rawData[0].spesh_time;
var gcTime = 0;
var gcNursery = 0;
var gcFull = 0;
var gcNurseryTime = 0;
var gcFullTime = 0;
var totalEntries = 0;
var inlineEntries = 0;
var jitEntries = 0;
var speshEntries = 0;
var deoptOnes = 0;
var deoptAlls = 0;
var osrs = 0;
rawData[0].gcs.map(function (gc) {
gcTime += gc.time;
if (gc.full) {
gcFull++;
gcFullTime += gc.time;
}
else {
gcNursery++;
gcNurseryTime += gc.time;
}
});
function walkCallGraphNode(node) {
totalEntries += node.entries || 0;
inlineEntries += node.inlined_entries || 0;
speshEntries += node.spesh_entries || 0;
jitEntries += node.jit_entries || 0;
deoptOnes += node.deopt_one || 0;
deoptAlls += node.deopt_all || 0;
osrs += node.osr || 0;
if (node.callees)
node.callees.map(walkCallGraphNode);
}
walkCallGraphNode(rawData[0].call_graph);
// Time spent
var overheadTime = speshTime + gcTime;
var executingTime = totalTime - overheadTime;
$scope.TotalTime = +(totalTime / 1000).toFixed(2);
$scope.OverheadTime = +(overheadTime / 1000).toFixed(2);
$scope.OverheadTimePercent = +(100 * overheadTime / totalTime).toFixed(2);
$scope.ExecutingTime = +(executingTime / 1000).toFixed(2);
$scope.ExecutingTimePercent = +(100 * executingTime / totalTime).toFixed(2);
$scope.GCTime = +(gcTime / 1000).toFixed(2);
$scope.GCTimePercent = +(100 * gcTime / totalTime).toFixed(2);
$scope.SpeshTime = +(speshTime / 1000).toFixed(2);
$scope.SpeshTimePercent = +(100 * speshTime / totalTime).toFixed(2);
// Routines
var interpEntries = totalEntries - (jitEntries + speshEntries);
$scope.EntriesWithoutInline = totalEntries - inlineEntries;
$scope.EntriesInline = inlineEntries;
$scope.InlinePercent = +(100 * inlineEntries / totalEntries).toFixed(2);
$scope.InterpFrames = interpEntries;
$scope.InterpFramesPercent = +(100 * interpEntries / totalEntries).toFixed(2);
$scope.SpeshFrames = speshEntries;
$scope.SpeshFramesPercent = +(100 * speshEntries / totalEntries).toFixed(2);
$scope.JITFrames = jitEntries;
$scope.JITFramesPercent = +(100 * jitEntries / totalEntries).toFixed(2);
// Garbage collection
$scope.GCRuns = gcNursery + gcFull;
$scope.FullGCRuns = gcFull;
$scope.NurseryAverage = +((gcNurseryTime / 1000) / gcNursery).toFixed(2);
$scope.FullAverage = +((gcFullTime / 1000) / gcFull).toFixed(2);
// Dynamic optimization
var optimizedFrames = speshEntries + jitEntries;
$scope.OptimizedFrames = optimizedFrames;
$scope.DeoptOnes = deoptOnes;
$scope.DeoptOnePercent = +(100 * deoptOnes / (optimizedFrames || 1)).toFixed(2);
$scope.DeoptAlls = deoptAlls;
$scope.OSRs = osrs;
});
moarProfApp.controller('RoutinesController', function ($scope) {
// Walk call graph to build data.
var idToEntries = {};
var idToSpeshEntries = {};
var idToJITEntries = {};
var idToExclusive = {};
var idToInclusive = {};
var idToOSR = {};
var idRecDepth = {};
var totalExclusive = 0;
var totalInclusive = rawData[0].call_graph.inclusive_time;
function walkCallGraphNode(node) {
if (!idToEntries[node.id]) {
idToEntries[node.id] = 0;
idToSpeshEntries[node.id] = 0;
idToJITEntries[node.id] = 0;
idToExclusive[node.id] = 0;
idToInclusive[node.id] = 0;
idToOSR[node.id] = false;
idRecDepth[node.id] = 0;
}
idToEntries[node.id] += node.entries || 0;
idToSpeshEntries[node.id] += node.spesh_entries || 0;
idToJITEntries[node.id] += node.jit_entries || 0;
idToExclusive[node.id] += node.exclusive_time || 0;
totalExclusive += node.exclusive_time || 0;
if (node.osr > 0)
idToOSR[node.id] = true;
if (idRecDepth[node.id] == 0)
idToInclusive[node.id] += node.inclusive_time;
if (node.callees) {
idRecDepth[node.id]++;
node.callees.map(walkCallGraphNode);
idRecDepth[node.id]--;
}
}
walkCallGraphNode(rawData[0].call_graph);
// Build object list per routine.
var routineList = [];
for (id in idToEntries) {
var speshEntriesPercent = +(100 * idToSpeshEntries[id] / idToEntries[id]).toFixed(2);
var jitEntriesPercent = +(100 * idToJITEntries[id] / idToEntries[id]).toFixed(2);
var interpEntriesPercent = 100 - (speshEntriesPercent + jitEntriesPercent);
var entry = {
Name: nodeIdToName[id],
Line: nodeIdToLine[id],
File: nodeIdToFile[id],
Entries: idToEntries[id],
InterpEntriesPercent: interpEntriesPercent,
SpeshEntriesPercent: speshEntriesPercent,
JITEntriesPercent: jitEntriesPercent,
InclusiveTime: +(idToInclusive[id] / 1000).toFixed(2),
InclusiveTimePercent: +(100 * idToInclusive[id] / totalInclusive).toFixed(2),
ExclusiveTime: +(idToExclusive[id] / 1000).toFixed(2),
ExclusiveTimePercent: +(100 * idToExclusive[id] / totalExclusive).toFixed(2),
OSR: idToOSR[id]
};
routineList.push(entry);
}
$scope.Routines = routineList;
$scope.predicate = "InclusiveTimePercent";
$scope.reverse = true;
});
moarProfApp.controller('CallGraphController', function ($scope) {
$scope.Current = rawData[0].call_graph;
$scope.total_time = rawData[0].total_time;
$scope.SuchCallers = false;
$scope.RecentCallers = [];
$scope.predicate = "TimePercent";
$scope.reverse = true;
var all_callers = [];
updateCurrentData();
$scope.toCallee = function (callee) {
// Update caller history.
all_callers.push($scope.Current);
$scope.RecentCallers.push($scope.Current);
if ($scope.RecentCallers.length > 5)
$scope.RecentCallers.shift();
// Update current node and callees.
$scope.Current = callee;
updateCurrentData();
};
$scope.toCaller = function (caller) {
// Update caller history.
while (all_callers.length > 0) {
var removed = all_callers.pop();
if ($scope.RecentCallers.length > 0)
$scope.RecentCallers.pop();
if (removed == caller)
break;
}
if (all_callers.length > $scope.RecentCallers.length) {
var ptr = all_callers.length - $scope.RecentCallers.length;
while (ptr >= 0 && $scope.RecentCallers.length < 5) {
$scope.RecentCallers.unshift(all_callers[ptr]);
ptr--;
}
}
// Update current node and callees.
$scope.Current = caller;
updateCurrentData();
}
/*
* Given a callee, create a unique, repeatable color;
* h/t https://stackoverflow.com/questions/3426404
*/
$scope.backgroundColor = function (callee) {
var str = callee.$$hashKey + callee.file + callee.name;
for (var i = 0, hash = 0; i < str.length; hash = str.charCodeAt(i++) + ((hash << 5) - hash));
for (var i = 0, colour = "#"; i < 3; colour += ("00" + ((hash >> i++ * 8) & 0xFF).toString(16)).slice(-2));
return colour;
}
function updateCurrentData() {
// Line and file.
var current = $scope.Current;
$scope.Line = current.line;
$scope.File = current.file;
// Entry statistics.
var interpEntries = current.entries - (current.spesh_entries + current.jit_entries);
var nonInlineEntries = current.entries - current.inlined_entries;
$scope.Entries = nonInlineEntries;
$scope.Percent = (100 * nonInlineEntries / current.entries).toFixed(2);
$scope.InlineEntries = current.inlined_entries;
$scope.InlinePercent = (100 * current.inlined_entries / current.entries).toFixed(2);
$scope.InterpEntries = interpEntries;
$scope.InterpPercent = (100 * interpEntries / current.entries).toFixed(2);
$scope.SpeshEntries = current.spesh_entries;
$scope.SpeshPercent = (100 * current.spesh_entries / current.entries).toFixed(2);
$scope.JITEntries = current.jit_entries;
$scope.JITPercent = (100 * current.jit_entries / current.entries).toFixed(2);
// Callees.
$scope.Callees = calleesOf(current);
}
function calleesOf(node) {
if (!node.callees)
return [];
var totalExclusive = 0.0;
node.callees.map(function (c) { totalExclusive += c.exclusive_time; });
return node.callees.map(function (c) {
var speshCallsPercent = +(100 * c.spesh_entries / c.entries).toFixed(2);
var jitCallsPercent = +(100 * c.jit_entries / c.entries).toFixed(2);
var interpCallsPercent = 100 - (speshCallsPercent + jitCallsPercent);
var inlinedPercent = +(100 * c.inlined_entries / c.entries).toFixed(2);
return {
Name: nodeIdToName[c.id],
Line: nodeIdToLine[c.id],
File: nodeIdToFile[c.id],
Calls: c.entries,
Time: +(c.inclusive_time / 1000).toFixed(2),
TimePercent: +(100 * c.inclusive_time / node.inclusive_time).toFixed(2),
InterpCallsPercent: interpCallsPercent,
SpeshCallsPercent: speshCallsPercent,
JITCallsPercent: jitCallsPercent,
InlinedPercent: inlinedPercent,
VeryInline: inlinedPercent >= 95,
SometimesInline: inlinedPercent < 95 && inlinedPercent > 10,
Node: c
};
});
}
});
moarProfApp.controller('AllocationsController', function ($scope, $modal) {
// Traverse all call nodes, counting up the allocations.
var typeIdToName = {};
var typeIdToAllocations = {};
var typeIdToAllocationsByType = {};
var typeIdToRoutineStats = {};
var maxAllocations = 1;
function walkCallGraphNode(node) {
if (node.allocations) {
node.allocations.map(function (alloc) {
alloc.type = id_to_things[alloc.id];
if (!typeIdToName[alloc.id]) {
typeIdToName[alloc.id] = alloc.type == "" ? "<anon>" : alloc.type;
typeIdToAllocations[alloc.id] = 0;
typeIdToAllocationsByType[alloc.id] = [0, 0, 0];
typeIdToRoutineStats[alloc.id] = {};
}
typeIdToAllocations[alloc.id] += alloc.count;
typeIdToAllocationsByType[alloc.id][0] += alloc.count - (alloc.spesh || 0) - (alloc.jit || 0);
typeIdToAllocationsByType[alloc.id][1] += alloc.spesh || 0;
typeIdToAllocationsByType[alloc.id][2] += alloc.jit || 0;
if (typeIdToAllocations[alloc.id] > maxAllocations)
maxAllocations = typeIdToAllocations[alloc.id];
if (typeIdToRoutineStats[alloc.id][node.id]) {
typeIdToRoutineStats[alloc.id][node.id]['count'] += alloc.count || 0;
typeIdToRoutineStats[alloc.id][node.id]['spesh'] += alloc.spesh || 0;
typeIdToRoutineStats[alloc.id][node.id]['jit'] += alloc.jit || 0;
} else {
typeIdToRoutineStats[alloc.id][node.id] = {
count: alloc.count || 0,
spesh: alloc.spesh || 0,
jit: alloc.jit || 0
};
}
});
}
if (node.callees) {
node.callees.map(walkCallGraphNode);
}
}
walkCallGraphNode(rawData[0].call_graph);
// Build allocation summary.
var allocationSummary = [];
for (id in typeIdToName) {
var maxAllocationByRoutine = 1;
for (var rid in typeIdToRoutineStats[id])
if (typeIdToRoutineStats[id][rid]['count'] > maxAllocationByRoutine)
maxAllocationByRoutine = typeIdToRoutineStats[id][rid]['count'];
var routineStats = [];
for (var rid in typeIdToRoutineStats[id])
routineStats.push({
Name: nodeIdToName[rid],
Line: nodeIdToLine[rid],
File: nodeIdToFile[rid],
Allocations: typeIdToRoutineStats[id][rid]['count'],
AllocationsSpesh: typeIdToRoutineStats[id][rid]['spesh'],
AllocationsJit: typeIdToRoutineStats[id][rid]['jit'],
AllocationsPercent: (100 * typeIdToRoutineStats[id][rid]['count'] / maxAllocationByRoutine),
AllocationsInterpPercent: (100 * (typeIdToRoutineStats[id][rid]['count'] - typeIdToRoutineStats[id][rid]['jit'] - typeIdToRoutineStats[id][rid]['spesh']) / maxAllocationByRoutine),
AllocationsSpeshPercent: (100 * typeIdToRoutineStats[id][rid]['spesh'] / maxAllocationByRoutine),
AllocationsJitPercent: (100 * typeIdToRoutineStats[id][rid]['jit'] / maxAllocationByRoutine)
});
var entry = {
Name: typeIdToName[id],
Allocations: typeIdToAllocations[id],
AllocationsPercent: +(100 * typeIdToAllocations[id] / maxAllocations).toFixed(2),
AllocationsInterpPercent: +(100 * typeIdToAllocationsByType[id][0] / maxAllocations).toFixed(2),
AllocationsSpeshPercent: +(100 * typeIdToAllocationsByType[id][1] / maxAllocations).toFixed(2),
AllocationsJitPercent: +(100 * typeIdToAllocationsByType[id][2] / maxAllocations).toFixed(2),
RoutineStats: routineStats
};
allocationSummary.push(entry);
}
$scope.AllocationSummary = allocationSummary;
$scope.predicate = "Allocations";
$scope.reverse = true;
$scope.routinePredicate = "Allocations";
$scope.routineReverse = true;
// Allocating routines handlng.
$scope.showAllocatingRoutines = function (alloc) {
// Show modal dialog with data.
$scope.CurrentAllocatingRoutine = alloc.Name;
$scope.CurrentAllocatingRoutineStats = alloc.RoutineStats;
var modalInstance = $modal.open({
templateUrl: 'myModalContent.html',
scope: $scope
});
}
});
moarProfApp.controller('GCController', function ($scope) {
// Find longest GC run.
var longestGC = 0;
rawData[0].gcs.map(function (gc) {
if (gc.time > longestGC)
longestGC = gc.time;
});
// Produce something nice to render.
var run = 0;
$scope.GCs = rawData[0].gcs.map(function (gc) {
var totalBytes = gc.cleared_bytes + gc.retained_bytes + gc.promoted_bytes;
return {
Run: ++run,
Time: +(gc.time / 1000).toFixed(2),
Full: (gc.full != 0),
TimePercent: +(100 * gc.time / longestGC).toFixed(2),
RetainedKilobytes: Math.round(gc.retained_bytes / 1024),
PromotedKilobytes: Math.round(gc.promoted_bytes / 1024),
ClearedKilobytes: Math.round(gc.cleared_bytes / 1024),
RetainedPercent: +(100 * gc.retained_bytes / totalBytes).toFixed(2),
PromotedPercent: +(100 * gc.promoted_bytes / totalBytes).toFixed(2),
ClearedPercent: +(100 * gc.cleared_bytes / totalBytes).toFixed(2),
Gen2Roots: 'gen2_roots' in gc ? gc.gen2_roots : 0
};
});
$scope.predicate = 'Run';
$scope.reverse = false;
});
moarProfApp.controller('OSRDeoptController', function ($scope) {
// Walk call graph to build data.
var idToOSR = {};
var idToDeoptOne = {};
var idToDeoptAll = {};
var maxOSR = 1;
var maxDeoptOne = 1;
var maxDeoptAll = 1;
function walkCallGraphNode(node) {
if (!idToOSR[node.id]) {
idToOSR[node.id] = 0;
idToDeoptOne[node.id] = 0;
idToDeoptAll[node.id] = 0;
}
idToOSR[node.id] += node.osr || 0;
idToDeoptOne[node.id] += node.deopt_one || 0;
idToDeoptAll[node.id] += node.deopt_all || 0;
if (idToOSR[node.id] > maxOSR)
maxOSR = idToOSR[node.id];
if (idToDeoptOne[node.id] > maxDeoptOne)
maxDeoptOne = idToDeoptOne[node.id];
if (idToDeoptAll[node.id] > maxDeoptAll)
maxDeoptAll = idToDeoptAll[node.id];
if (node.callees)
node.callees.map(walkCallGraphNode);
}
walkCallGraphNode(rawData[0].call_graph);
// Build up OSR, deopt one, and deopt all tables.
var osrs = [];
var deoptOnes = [];
var deoptAlls = [];
for (id in idToOSR) {
if (idToOSR[id] > 0) {
osrs.push({
Name: nodeIdToName[id],
Line: nodeIdToLine[id],
File: nodeIdToFile[id],
Count: idToOSR[id],
Percent: Math.round(100 * idToOSR[id] / maxOSR)
});
}
if (idToDeoptOne[id] > 0) {
deoptOnes.push({
Name: nodeIdToName[id],
Line: nodeIdToLine[id],
File: nodeIdToFile[id],
Count: idToDeoptOne[id],
Percent: Math.round(100 * idToDeoptOne[id] / maxDeoptOne)
});
}
if (idToDeoptAll[id] > 0) {
deoptAlls.push({
Name: nodeIdToName[id],
Line: nodeIdToLine[id],
File: nodeIdToFile[id],
Count: idToDeoptAll[id],
Percent: Math.round(100 * idToDeoptAll[id] / maxDeoptAll)
});
}
}
$scope.OSRs = osrs;
$scope.DeoptOnes = deoptOnes;
$scope.DeoptAlls = deoptAlls;
$scope.predicate = 'Count';
$scope.reverse = true;
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment