Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
939ac5f
CPU/Memory logging working
ChrisPaulBennett Feb 24, 2025
0bdd49a
Time series now working
ChrisPaulBennett Mar 3, 2025
f49aa12
Fixed Chart Labels
ChrisPaulBennett Mar 4, 2025
75b9add
Added "Total of Totals" for CPU time.
ChrisPaulBennett Mar 10, 2025
33f24db
Time series Y axis labels fixed.
ChrisPaulBennett Mar 12, 2025
00c5c56
Removed console.log commands
ChrisPaulBennett Mar 12, 2025
c5f8cb4
Updated formatDuration to properly handle the lack of optional arguments
ChrisPaulBennett Mar 12, 2025
d9c8ed3
Adding towncrier fragment
ChrisPaulBennett Apr 2, 2025
04c59fa
Merge branch 'refs/heads/origin-master' into memory_logging
ChrisPaulBennett Apr 4, 2025
eceb33b
Merge branch 'master' into memory_logging
ChrisPaulBennett Jun 9, 2025
1c7aca8
Update src/views/Analysis.vue
ChrisPaulBennett Jul 11, 2025
36db262
Refactoring loop
ChrisPaulBennett Jul 11, 2025
b98ae2b
Merge remote-tracking branch 'cylc_ui_fork/memory_logging' into memor…
ChrisPaulBennett Jul 11, 2025
3de0518
Update src/views/Analysis.vue
ChrisPaulBennett Jul 11, 2025
be7836f
Adding unit test coverage
ChrisPaulBennett Jul 14, 2025
e8133d0
Update src/utils/tasks.js
ChrisPaulBennett Jul 18, 2025
1e60a84
Merge branch 'refs/heads/master' into memory_logging
ChrisPaulBennett Jul 21, 2025
a216ce3
Code review changes
ChrisPaulBennett Jul 23, 2025
65d3aac
Merge branch 'refs/heads/master' into memory_logging
ChrisPaulBennett Jul 28, 2025
7812385
Changed table view behaviour to allow zeros
ChrisPaulBennett Jul 31, 2025
51b0c2b
Add memory allocation
ChrisPaulBennett Sep 11, 2025
e5dfeee
Added annotations as a way to show Allocated RSS
samuel-denton Sep 23, 2025
c76ca03
Changing how MaxRSS is formatted
ChrisPaulBennett Sep 24, 2025
e20fdaa
Refining the memory allocated display
ChrisPaulBennett Sep 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes.d/2100.feat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Adding CPU time and Max RSS to Analysis Tools
Binary file added public/img/redline.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
120 changes: 75 additions & 45 deletions src/components/cylc/analysis/AnalysisTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.

<script>
import { upperFirst } from 'lodash'
import { formatDuration } from '@/utils/tasks'
import {
formatDuration,
formatHeader,
formatChartLabels,
} from '@/utils/tasks'
import {
initialOptions,
updateInitialOptionsEvent,
Expand Down Expand Up @@ -128,50 +132,76 @@ export default {
computed: {
shownHeaders () {
const times = upperFirst(this.timingOption)
const timingHeaders = [
{
title: `Mean T-${times}`,
key: `mean${times}Time`,
formatter: formatDuration,
allowZeros: false
},
{
title: `Std Dev T-${times}`,
key: `stdDev${times}Time`,
formatter: formatDuration,
allowZeros: true
},
{
title: `Min T-${times}`,
key: `min${times}Time`,
formatter: formatDuration,
allowZeros: false
},
{
title: `Q1 T-${times}`,
key: `${times.toLowerCase()}Quartiles.0`,
formatter: formatDuration,
allowZeros: false
},
{
title: `Median T-${times}`,
key: `${times.toLowerCase()}Quartiles.1`,
formatter: formatDuration,
allowZeros: false
},
{
title: `Q3 T-${times}`,
key: `${times.toLowerCase()}Quartiles.2`,
formatter: formatDuration,
allowZeros: false
},
{
title: `Max T-${times}`,
key: `max${times}Time`,
const timingHeaders = []
// Check if there are any stats to show
const stats = this.tasks.some((task) => task.count > 1)
if (stats) {
timingHeaders.push(
{
title: `Mean ${formatChartLabels(times)}`,
key: `${formatHeader('mean', times)}`,
formatter: formatDuration,
allowZeros: true,
timingOption: this.timingOption
},
{
title: `Min ${formatChartLabels(times)}`,
key: `${formatHeader('min', times)}`,
formatter: formatDuration,
allowZeros: true,
timingOption: this.timingOption
},
{
title: `Q1 ${formatChartLabels(times)}`,
key: `${formatHeader('quartiles', times)}Quartiles.0`,
formatter: formatDuration,
allowZeros: true,
timingOption: this.timingOption
},
{
title: `Median ${formatChartLabels(times)}`,
key: `${formatHeader('quartiles', times)}Quartiles.1`,
formatter: formatDuration,
allowZeros: true,
timingOption: this.timingOption
},
{
title: `Q3 ${formatChartLabels(times)}`,
key: `${formatHeader('quartiles', times)}Quartiles.2`,
formatter: formatDuration,
allowZeros: true,
timingOption: this.timingOption
},
{
title: `Max ${formatChartLabels(times)}`,
key: `${formatHeader('max', times)}`,
formatter: formatDuration,
allowZeros: true,
timingOption: this.timingOption
}
)
} else {
timingHeaders.push(
{
title: `${formatChartLabels(times)}`,
key: `${formatHeader('mean', times)}`,
formatter: formatDuration,
allowZeros: true,
timingOption: this.timingOption
}
)
}

// Don't show std dev for cpuTime or maxRss
if (this.timingOption !== 'cpuTime' && this.timingOption !== 'maxRss' && stats) {
timingHeaders.push({
title: `Std Dev ${times}`,
key: `${formatHeader('stdDev', times)}`,
formatter: formatDuration,
allowZeros: false
}
]
allowZeros: true,
timingOption: this.timingOption
})
}
return this.headers.concat(timingHeaders)
}
},
Expand All @@ -186,7 +216,7 @@ export default {
value = value[index]
}
if (header.formatter) {
return header.formatter(value, header.allowZeros)
return header.formatter(value, header.allowZeros, this.timingOption)
}
return value
}
Expand Down
197 changes: 126 additions & 71 deletions src/components/cylc/analysis/BoxPlot.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
:height="105 + series[0].data.length * 60"
width="95%"
class="d-flex justify-center"
/>
/>
<v-pagination
v-model="page"
:length="numPages"
Expand All @@ -65,7 +65,11 @@ import {
mdiSortVariant,
} from '@mdi/js'
import { upperFirst } from 'lodash'
import { formatDuration } from '@/utils/tasks'
import {
formatDuration,
getTimingOption,
formatChartLabels
} from '@/utils/tasks'
import { useReducedAnimation } from '@/composables/localStorage'
import {
initialOptions,
Expand Down Expand Up @@ -128,79 +132,130 @@ export default {

const reducedAnimation = useReducedAnimation()

const chartOptions = computed(() => ({
chart: {
defaultLocale: 'en',
locales: [
{
name: 'en',
options: {
toolbar: {
exportToSVG: 'Download SVG',
exportToPNG: 'Download PNG',
menu: 'Download'
const compare = (a, b) => {
const ret = a[sortBy.value] < b[sortBy.value] ? -1 : 1
return sortDesc.value ? -ret : ret
}

const chartOptions = computed(() => {
const currentTasks = (() => {
if (props.timingOption === 'maxRss') {
const sortedTasks = [...props.tasks].sort((a, b) => compare(a, b))
const startIndex = Math.max(0, props.itemsPerPage * (page.value - 1))
const endIndex = Math.min(sortedTasks.length, startIndex + props.itemsPerPage)
return sortedTasks.slice(startIndex, endIndex)
}
return []
})()

return {
chart: {
defaultLocale: 'en',
locales: [
{
name: 'en',
options: {
toolbar: {
exportToSVG: 'Download SVG',
exportToPNG: 'Download PNG',
menu: 'Download'
}
}
}
}
],
animations: {
enabled: reducedAnimation.value ? false : props.animate,
easing: 'easeinout',
speed: 300,
animateGradually: {
enabled: true,
delay: 150,
},
dynamicAnimation: {
enabled: true,
speed: 350,
],
animations: {
enabled: reducedAnimation.value ? false : props.animate,
easing: 'easeinout',
speed: 300,
animateGradually: {
enabled: true,
delay: 150,
},
dynamicAnimation: {
enabled: true,
speed: 350,
},
},
},
fontFamily: 'inherit',
toolbar: {
tools: {
download: `<svg class="w-100 h-100"><path d="${mdiDownload}"></path></svg>`,
fontFamily: 'inherit',
toolbar: {
tools: {
download: `<svg class="w-100 h-100"><path d="${mdiDownload}"></path></svg>`,
},
},
},
},
tooltip: {
custom ({ seriesIndex, dataPointIndex, w }) {
const max = formatDuration(w.globals.seriesCandleC[seriesIndex][dataPointIndex], true)
const q3 = formatDuration(w.globals.seriesCandleL[seriesIndex][dataPointIndex], true)
const med = formatDuration(w.globals.seriesCandleM[seriesIndex][dataPointIndex], true)
const q1 = formatDuration(w.globals.seriesCandleH[seriesIndex][dataPointIndex], true)
const min = formatDuration(w.globals.seriesCandleO[seriesIndex][dataPointIndex], true)
return `
<div class="pa-2">
tooltip: {
custom ({ seriesIndex, dataPointIndex, w }) {
const max = formatDuration(w.globals.seriesCandleC[seriesIndex][dataPointIndex], true, props.timingOption)
const q3 = formatDuration(w.globals.seriesCandleL[seriesIndex][dataPointIndex], true, props.timingOption)
const med = formatDuration(w.globals.seriesCandleM[seriesIndex][dataPointIndex], true, props.timingOption)
const q1 = formatDuration(w.globals.seriesCandleH[seriesIndex][dataPointIndex], true, props.timingOption)
const min = formatDuration(w.globals.seriesCandleO[seriesIndex][dataPointIndex], true, props.timingOption)
if (props.timingOption === 'maxRss') {
const memAlloc = formatDuration(w.globals.series[seriesIndex][dataPointIndex], true, props.timingOption)
return `
<div class="pa-2">
<div>Maximum: ${max}</div>
<div>Q3: ${q3} </div>
<div>Median: ${med}</div>
<div>Q1: ${q1}</div>
<div>Minimum: ${min}</div>
</div>
`
<div>Memory Allocated: ${memAlloc}</div>
</div>
`
} else {
return `
<div class="pa-2">
<div>Maximum: ${max}</div>
<div>Q3: ${q3} </div>
<div>Median: ${med}</div>
<div>Q1: ${q1}</div>
<div>Minimum: ${min}</div>
</div>
`
}
},
},
},
plotOptions: {
bar: {
horizontal: true,
annotations: {
position: 'front',
points: currentTasks.map(point => ({
x: point.memAlloc,
y: point.name,
marker: {
size: 0 // Hides the default marker
},
image: {
path: 'img/redline.jpg',
width: 40,
height: 60,
offsetX: 0,
offsetY: -3,
}
}))
},
boxPlot: {
colors: {
upper: '#6DD5C2',
lower: '#6AA4F1',
plotOptions: {
bar: {
horizontal: true,
},
boxPlot: {
colors: {
upper: '#6DD5C2',
lower: '#6AA4F1',
},
},
},
},
xaxis: {
title: {
text: `${upperFirst(props.timingOption)} time`,
},
labels: {
formatter: (value) => formatDuration(value, true)
xaxis: {
min: currentTasks.length > 0 ? 0 : undefined,
max: currentTasks.length > 0 ? Math.ceil(Math.max(0, ...currentTasks.map(t => t.memAlloc)) * 1.05) : undefined,
type: 'numeric',
title: {
text: `${formatChartLabels(props.timingOption)}`,
},
labels: {
formatter: (value) => formatDuration(value, true, props.timingOption)
},
},
},
}))
}
})

return {
sortBy,
Expand All @@ -215,21 +270,21 @@ export default {
const sortedTasks = [...this.tasks].sort(this.compare)
const startIndex = Math.max(0, this.itemsPerPage * (this.page - 1))
const endIndex = Math.min(sortedTasks.length, startIndex + this.itemsPerPage)

const data = []
const boxData = []
for (let i = startIndex; i < endIndex; i++) {
data.push({
boxData.push({
x: sortedTasks[i].name,
y: [
sortedTasks[i][`min${upperFirst(this.timingOption)}Time`],
sortedTasks[i][`min${upperFirst(getTimingOption(this.timingOption))}`],
sortedTasks[i][`${this.timingOption}Quartiles`][0],
sortedTasks[i][`${this.timingOption}Quartiles`][1],
sortedTasks[i][`${this.timingOption}Quartiles`][2],
sortedTasks[i][`max${upperFirst(this.timingOption)}Time`],
],
sortedTasks[i][`max${upperFirst(getTimingOption(this.timingOption))}`],
sortedTasks[i].memAlloc > 0 ? sortedTasks[i].memAlloc : 1 // ApexCharts seems inconsistent if this extra data is 0
]
})
}
return [{ data }]
return [{ name: 'boxPlot', type: 'boxPlot', data: boxData }]
},

numPages () {
Expand All @@ -241,10 +296,10 @@ export default {
{ title: 'Task name', value: 'name' },
{ title: 'Platform', value: 'platform' },
{ title: 'Count', value: 'count' },
{ title: `Mean ${this.timingOption} time`, value: `mean${upperFirst(this.timingOption)}Time` },
{ title: `Median ${this.timingOption} time`, value: `median${upperFirst(this.timingOption)}Time` },
{ title: `Min ${this.timingOption} time`, value: `min${upperFirst(this.timingOption)}Time` },
{ title: `Max ${this.timingOption} time`, value: `max${upperFirst(this.timingOption)}Time` },
{ title: `Mean ${formatChartLabels(this.timingOption)}`, value: `mean${upperFirst(getTimingOption(this.timingOption))}` },
{ title: `Median ${formatChartLabels(this.timingOption)}`, value: `median${upperFirst(getTimingOption(this.timingOption))}` },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what is happening here but in the offline mode, sorting by median time is not working for any metric

Image

Possibly missing from offline data?

{ title: `Min ${formatChartLabels(this.timingOption)}`, value: `min${upperFirst(getTimingOption(this.timingOption))}` },
{ title: `Max ${formatChartLabels(this.timingOption)}`, value: `max${upperFirst(getTimingOption(this.timingOption))}` },
]
},
},
Expand Down
Loading