Line data Source code
1 : import * as dashql from '@ankoh/dashql-core';
2 :
3 : import * as React from 'react';
4 : import * as Immutable from 'immutable';
5 :
6 : import { formatBytes, formatNanoseconds } from '../../utils/format.js';
7 : import { classNames } from '../../utils/classnames.js';
8 :
9 : import * as styles from './script_statistics_bar.module.css';
10 :
11 : interface HistoryProps {
12 : data: Float64Array;
13 : maximum: number;
14 : }
15 :
16 1 : const History: React.FC<HistoryProps> = (props: HistoryProps) => {
17 1 : const out = [];
18 1 : const scaledMax = props.maximum * 1.2;
19 1 : for (let i = 0; i < props.data.length; ++i) {
20 1 : const percent = (props.data[i] * 100) / scaledMax;
21 1 : out.push(
22 1 : <div
23 1 : key={i}
24 1 : style={{
25 1 : height: `${percent}%`,
26 1 : backgroundColor: 'var(--color_border)',
27 1 : }}
28 1 : />,
29 1 : );
30 1 : }
31 1 : return <div className={styles.metric_history}>{out}</div>;
32 1 : };
33 :
34 : interface Props {
35 : className?: string;
36 : stats: Immutable.List<dashql.buffers.editor.EditorProcessingStatisticsT> | null;
37 : }
38 :
39 1 : export const ScriptStatisticsBar: React.FC<Props> = (props: Props) => {
40 1 : const stats = props.stats ?? Immutable.List();
41 1 : if (stats.isEmpty()) {
42 1 : return (
43 1 : <div className={classNames(props.className, styles.container)}>
44 1 : <div className={styles.metric_container}>
45 1 : <div className={styles.metric_last_reading}>- us</div>
46 1 : </div>
47 1 : <div className={styles.metric_container}>
48 1 : <div className={styles.metric_last_reading}>- bytes</div>
49 1 : </div>
50 1 : </div>
51 : );
52 : }
53 :
54 1 : const computeTotalMemory = (reading: dashql.buffers.editor.EditorProcessingStatisticsT) =>
55 1 : Number(reading.ropeBytes
56 1 : + reading.scannerInputBytes
57 1 : + reading.scannerNameDictionaryBytes
58 1 : + reading.parserAstBytes
59 1 : + reading.analyzerDescriptionBytes
60 1 : + reading.analyzerNameIndexBytes);
61 :
62 1 : const last = stats.last()!;
63 1 : const lastElapsedScanner = last.scannerLastElapsedNs;
64 1 : const lastElapsedParser = last.parserLastElapsedNs;
65 1 : const lastElapsedAnalyzer = last.analyzerLastElapsedNs;
66 1 : const lastTotalMemory = computeTotalMemory(last);
67 :
68 1 : const n = Math.min(stats.size, 20);
69 1 : const bufferSize = Math.max(n, 20);
70 1 : const elapsedScannerHistory = new Float64Array(bufferSize);
71 1 : const elapsedParserHistory = new Float64Array(bufferSize);
72 1 : const elapsedAnalyzerHistory = new Float64Array(bufferSize);
73 1 : const memoryHistory = new Float64Array(bufferSize);
74 1 : let maxElapsedScanner = 0;
75 1 : let maxElapsedParser = 0;
76 1 : let maxElapsedAnalyzer = 0;
77 1 : let maxTotalMemory = 0;
78 1 : let writer = 0;
79 1 : for (const reading of stats.toSeq().take(n)) {
80 1 : const totalMemory = computeTotalMemory(reading);
81 1 : const elapsedScanner = reading.scannerLastElapsedNs;
82 1 : const elapsedParser = reading.parserLastElapsedNs;
83 1 : const elapsedAnalyzer = reading.analyzerLastElapsedNs;
84 1 : elapsedScannerHistory[writer] = elapsedScanner;
85 1 : elapsedParserHistory[writer] = elapsedParser;
86 1 : elapsedAnalyzerHistory[writer] = elapsedAnalyzer;
87 1 : memoryHistory[writer] = totalMemory;
88 1 : maxElapsedScanner = Math.max(maxElapsedScanner, elapsedScanner);
89 1 : maxElapsedParser = Math.max(maxElapsedParser, elapsedParser);
90 1 : maxElapsedAnalyzer = Math.max(maxElapsedAnalyzer, elapsedAnalyzer);
91 1 : maxTotalMemory = Math.max(maxTotalMemory, totalMemory);
92 1 : ++writer;
93 1 : }
94 :
95 1 : return (
96 1 : <div className={classNames(props.className, styles.container)}>
97 1 : <div className={styles.metric_container}>
98 1 : <History data={memoryHistory} maximum={maxTotalMemory} />
99 1 : <div className={styles.metric_text_overlay}>
100 1 : <div className={styles.metric_name}>M</div>
101 1 : <div className={styles.metric_last_reading}>{formatBytes(lastTotalMemory)}</div>
102 1 : </div>
103 1 : </div>
104 1 : <div className={styles.metric_container}>
105 1 : <History data={elapsedScannerHistory} maximum={maxElapsedScanner} />
106 1 : <div className={styles.metric_text_overlay}>
107 1 : <div className={styles.metric_name}>S</div>
108 1 : <div className={styles.metric_last_reading}>{formatNanoseconds(lastElapsedScanner)}</div>
109 1 : </div>
110 1 : </div>
111 1 : <div className={styles.metric_container}>
112 1 : <History data={elapsedParserHistory} maximum={maxElapsedParser} />
113 1 : <div className={styles.metric_text_overlay}>
114 1 : <div className={styles.metric_name}>P</div>
115 1 : <div className={styles.metric_last_reading}>{formatNanoseconds(lastElapsedParser)}</div>
116 1 : </div>
117 1 : </div>
118 1 : <div className={styles.metric_container}>
119 1 : <History data={elapsedAnalyzerHistory} maximum={maxElapsedAnalyzer} />
120 1 : <div className={styles.metric_text_overlay}>
121 1 : <div className={styles.metric_name}>A</div>
122 1 : <div className={styles.metric_last_reading}>{formatNanoseconds(lastElapsedAnalyzer)}</div>
123 1 : </div>
124 1 : </div>
125 1 : </div>
126 : );
127 1 : };
|