-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathPerformance.fs
275 lines (202 loc) · 10.1 KB
/
Performance.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
module reMarkable.fs.Unix.Driver.Performance
open System
open System.Collections.Generic
open System.IO
open System.Text.RegularExpressions
/// Provides an interface through which the system performance can be profiled
type IPerformanceMonitor =
/// Gets the number of total logical cores in the device
abstract member NumberOfCores: int
/// Gets the number of physical processors in the device
abstract member NumberOfProcessors: int
/// Gets the total amount of memory in the device, in bytes
abstract member TotalMemory: int64
/// Gets the total amount of swap in the device, in bytes
abstract member TotalSwap: int64
/// Gets the total amount of free memory, in bytes
abstract member GetFreeMemory: unit-> int64
/// Gets the total amount of free swap, in bytes
abstract member GetFreeSwap: unit -> int64
/// Lists all of the available network adapters
abstract member GetNetworkAdapters: unit -> string array
/// Gets the instantaneous network download speed, in bytes/second
abstract member GetNetworkRxSpeed: adapter: string -> int64
/// Gets the total network download utilization, in bytes
abstract member GetNetworkRxTotal: adapter: string -> int64
/// Gets the instantaneous network upload speed, in bytes/second
abstract member GetNetworkTxSpeed: adapter: string -> int64
/// Gets the total network upload utilization, in bytes
abstract member GetNetworkTxTotal: adapter: string -> int64
/// Gets the total processor utilization, from 0-1
abstract member GetProcessorTime: unit -> float32
/// Gets a specific processor's utilization, from 0-1
abstract member GetProcessorTime: processor: int -> float32
/// Gets a specific core's utilization, from 0-1
abstract member GetProcessorTime: processor:int * core:int -> float32
/// Contains data related to the statistics of a network adapter
type NetDeviceInfo =
{ RxBytes: int64
RxCompressed: int64
RxDrop: int64
RxErrs: int64
RxFifo: int64
RxFrame: int64
RxMulticast: int64
RxPackets: int64
TxBytes: int64
TxCompressed: int64
TxDrop: int64
TxErrs: int64
TxFifo: int64
TxFrame: int64
TxMulticast: int64
TxPackets: int64 }
/// Provides methods for calculating static and time-based performance metrics
type PerformanceMeasurement() =
let mutable _previousTime: DateTime = DateTime.Now // ?
let mutable _previousValue: double = Double.MinValue //?
/// Calculates a differential based on the previous and new values
/// <param name="measurement">The new value to compare to the old value</param>
/// <returns>The calculated delta</returns>
member _.PushMeasurement (measurement: double): double =
let dM = measurement - _previousValue
_previousValue <- measurement
dM
/// Calculates a time-based differential based on the previous and new values and the previous and current time
/// <param name="measurement">The new value to compare to the old value</param>
/// <returns>The calculated delta in units per second</returns>
member _.PushMeasurementPerSecond(measurement: double): double =
let time = DateTime.Now
let dT = time - _previousTime
let dM = measurement - _previousValue
_previousTime <- time
_previousValue <- measurement
dM / dT.TotalSeconds
let private getNetworkMeasurements(): Dictionary<string, NetDeviceInfo> =
let result = Dictionary<string, NetDeviceInfo>()
use sr = new StreamReader("/proc/net/dev")
// 2 header lines - throw them away
sr.ReadLine() |> ignore
sr.ReadLine() |> ignore
while not sr.EndOfStream do
let line = sr.ReadLine()
if line = null then
()
else
let columns = Regex.Split(line.Trim(), "\\s+")
let info: NetDeviceInfo =
{ RxBytes = Int64.Parse(columns.[1])
RxCompressed = Int64.Parse(columns.[7])
RxDrop = Int64.Parse(columns.[4])
RxErrs = Int64.Parse(columns.[3])
RxFifo = Int64.Parse(columns.[5])
RxFrame = Int64.Parse(columns.[6])
RxMulticast = Int64.Parse(columns.[8])
RxPackets = Int64.Parse(columns.[2])
TxBytes = Int64.Parse(columns.[9])
TxCompressed = Int64.Parse(columns.[15])
TxDrop = Int64.Parse(columns.[12])
TxErrs = Int64.Parse(columns.[11])
TxFifo = Int64.Parse(columns.[13])
TxFrame = Int64.Parse(columns.[14])
TxMulticast = Int64.Parse(columns.[16])
TxPackets = Int64.Parse(columns.[10]) }
let mutable adapterName = columns.[0]
adapterName <- adapterName.Remove(adapterName.Length - 1)
result.Add(adapterName, info)
result
/// Contains data associated with an instantaneous CPU measurement
///
/// <param name="total">The total processor time for this sample</param>
/// <param name="idle">The idle processor time for this sample</param>
type CpuMeasurement(total: int64, idle: int64) = // todo: maybe a record?
/// The idle processor time for this sample
member _.Idle = idle
/// The total processor time for this sample
member _.Total = total
let private getCpuMeasurements(): Dictionary<string, CpuMeasurement> =
let result = Dictionary<string, CpuMeasurement>()
use sr = new StreamReader("/proc/stat")
while not sr.EndOfStream do
let line = sr.ReadLine()
match line with
| null -> ()
| line when line.StartsWith "cpu" ->
let columns = Regex.Split(line, "\\s+")
let cpuIndex = columns.[0]
let totalTime = columns |> Seq.tail |> Seq.sumBy Int64.Parse
let idleTime = Int64.Parse(columns.[4])
result.Add(cpuIndex, CpuMeasurement(totalTime, idleTime))
| _ -> ()
result
let private getMemoryMeasurements(): Dictionary<string, int64> =
let result = Dictionary<string, int64>()
use sr = new StreamReader("/proc/meminfo")
while not sr.EndOfStream do
let line = sr.ReadLine()
if line = null then
() // todo: return early?
else
let regexMatch = Regex.Match(line, "(.+):\\s+(\\d+) kB")
if not regexMatch.Success then
()
else
// Kilobytes
result.Add(regexMatch.Groups.[1].Value, Int64.Parse(regexMatch.Groups.[2].Value) * (int64 1000))
result
/// Provides a set of methods to profile hardware performance metrics
type HardwarePerformanceMonitor() =
/// Contains the instantaneous CPU time-based measurements
let globalCpuMeasurements = Dictionary<string, PerformanceMeasurement>()
/// Contains the instantaneous network time-based measurements
let globalNetworkMeasurements = Dictionary<string, PerformanceMeasurement>()
let globalMemoryMeasurements = getMemoryMeasurements()
do
for keyValuePair in getCpuMeasurements() do
globalCpuMeasurements.Add($"idle-{keyValuePair.Key}", PerformanceMeasurement())
globalCpuMeasurements.Add($"total-{keyValuePair.Key}", PerformanceMeasurement())
for keyValuePair in getNetworkMeasurements() do
globalNetworkMeasurements.Add($"tx-{keyValuePair.Key}", PerformanceMeasurement())
globalNetworkMeasurements.Add($"rx-{keyValuePair.Key}", PerformanceMeasurement())
interface IPerformanceMonitor with
member _.NumberOfCores = globalCpuMeasurements.Count - 1
member _.NumberOfProcessors = 1
member _.TotalMemory = globalMemoryMeasurements.["MemTotal"]
member _.TotalSwap = globalMemoryMeasurements.["SwapTotal"]
member _.GetFreeMemory() =
let measurements = getMemoryMeasurements()
measurements.["MemAvailable"]
member _.GetFreeSwap() =
let measurements = getMemoryMeasurements()
measurements.["SwapFree"]
member _.GetNetworkAdapters () = (getNetworkMeasurements()).Keys |> Seq.toArray
member _.GetNetworkRxSpeed adapter =
let nowBytes = (getNetworkMeasurements()).[adapter].RxBytes |> double
let measurement = globalNetworkMeasurements.[$"rx-{adapter}"].PushMeasurementPerSecond nowBytes
int64 measurement
member _.GetNetworkRxTotal adapter =
(getNetworkMeasurements()).[adapter].RxBytes
member _.GetNetworkTxSpeed(adapter) =
let nowBytes = (getNetworkMeasurements()).[adapter].TxBytes |> double
let measurement = globalNetworkMeasurements.[$"tx-{adapter}"].PushMeasurementPerSecond(nowBytes)
int64 measurement
member _.GetNetworkTxTotal adapter =
(getNetworkMeasurements()).[adapter].TxBytes
member _.GetProcessorTime() =
let cpuMeasurements = getCpuMeasurements()
let cpuTotal = cpuMeasurements.["cpu"]
let idle = globalCpuMeasurements.["idle-cpu"].PushMeasurement(double cpuTotal.Idle)
let total = globalCpuMeasurements.["total-cpu"].PushMeasurement(double cpuTotal.Total)
float32 ((double 1) - idle / total)
member _.GetProcessorTime(processor, core) =
if (processor <> 0) then
raise <| ArgumentException(nameof processor)
let cpuMeasurements = getCpuMeasurements()
let cpuTotal = cpuMeasurements.["cpu"]
let idle = globalCpuMeasurements.[$"idle-cpu{core}"].PushMeasurement(double cpuTotal.Idle)
let total = globalCpuMeasurements.[$"total-cpu{core}"].PushMeasurement(double cpuTotal.Total)
float32 ((double 1) - idle / total)
member this.GetProcessorTime(processor) =
if processor <> 0 then
raise <| ArgumentException(nameof processor)
(this :> IPerformanceMonitor).GetProcessorTime()