-
Notifications
You must be signed in to change notification settings - Fork 0
/
Invoke-Load.ps1
184 lines (137 loc) · 5.66 KB
/
Invoke-Load.ps1
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
<#
(c) 2019 Nenad Noveljic All Rights Reserved
Usage: Invoke-Load -Concurrency n
Version 2.4
Prerequisites:
sp_cpu_loop in the database, configure connect string in Config.psd1
It runs a sp_cpu_loop with n concurrent sessions and measures elapsed time,
SOS_SCHEDULER_YIELD wait time and CPU time on each scheduler
#>
param (
[int]$Concurrency = 1,
[String]$ConfigFileName = "Config.psd1"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$parallel = $concurrency
function ExecuteSelect
{
param(
$Connection,
[string]$Select
)
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.Connection = $Connection
$SqlCmd.CommandText = $Select
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter
$dataset = New-Object System.Data.DataSet
$adapter.SelectCommand = $SqlCmd
$adapter.Fill($dataSet) | Out-Null
$dataSet.Tables | Format-Table
}
$ConfigFile = Import-LocalizedData -BaseDirectory . -FileName $ConfigFileName
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$ConnectString = $ConfigFile.ConnectString
$SqlConnection.ConnectionString = $ConnectString
$SQlConnection.Open()
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.Connection = $SqlConnection
$SqlCmd.CommandText = "drop table if exists os_schedulers_before"
$SqlCmd.ExecuteNonQuery() | Out-Null
$SqlCmd.CommandText = "drop table if exists os_schedulers_after"
$SqlCmd.ExecuteNonQuery() | Out-Null
# Snapshot tables instead of CLEAR, because you might not have the provilege
# to clear, like for example in Azure
$SqlCmd.CommandText = "drop table if exists os_waits_before"
$SqlCmd.ExecuteNonQuery() | Out-Null
$SqlCmd.CommandText = "drop table if exists os_waits_after"
$SqlCmd.ExecuteNonQuery() | Out-Null
$SqlCmd.CommandText = "select * into os_schedulers_before from sys.dm_os_schedulers"
$SqlCmd.ExecuteNonQuery() | Out-Null
$SqlCmd.CommandText = "select * into os_waits_before from sys.dm_os_wait_stats"
$SqlCmd.ExecuteNonQuery() | Out-Null
$Load = $ConfigFile.Load
#$LOOP_ITERATIONS = 10000000
Get-Job | Remove-Job | Out-Null
For ( $i = 1 ; $i -le $parallel ; $i++ ) {
#$Input = [System.Tuple]::Create($ConnectString, $LOOP_ITERATIONS, $i)
$Input = [System.Tuple]::Create($ConnectString, $Load, $i)
Start-Job -Name "SQL$i" -ArgumentList $Input -ScriptBlock {
$args[0] | Measure-Command {
$ConnectString = $_.Item1
#$LOOP_ITERATIONS = $_.Item2
$Load = $_.Item2
$proc_id = $_.Item3
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = $ConnectString
$SQlConnection.Open()
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.Connection = $SqlConnection
#$SqlCmd.CommandText = "exec sp_cpu_loop @iterations = " + $LOOP_ITERATIONS
$SqlCmd.CommandText = $Load
$SqlCmd.CommandTimeout = 1000
$SqlCmd.ExecuteNonQuery() | Out-Null
$SqlConnection.Close()
}
} | Out-Null
}
$times_arr = @()
Write-Host "Threads: " $PARALLEL
Write-Host "Elapsed times:"
For ( $i = 1 ; $i -le $PARALLEL ; $i++ ) {
Wait-Job "SQL$i" | Out-Null
$out = Receive-Job "SQL$i" | findstr -i TotalMilliseconds
$time = $out -replace "TotalMilliseconds\s+:\s+(\S+)", '$1'
Write-Host $time
$times_arr += $time
Remove-Job "SQL$i"
}
$times_arr | Measure-Object -Average -Maximum -Minimum -Sum
$mean = $times_arr | Measure-Object -Average | select -ExpandProperty Average
$sqdiffs = $times_arr | foreach {[math]::Pow(($psitem - $mean), 2)}
$sigma = [math]::Sqrt( ($sqdiffs | Measure-Object -Average | select -ExpandProperty Average) )
$sigma = [math]::Round($sigma, 3)
Write-Host "Standard Deviation:" $sigma
<#
$iterations_per_s = $times_arr | foreach { $LOOP_ITERATIONS / $_ }
$iterations_per_s_total = $iterations_per_s | Measure-Object -Sum | select -ExpandProperty Sum
$iterations_per_s_total = [math]::Round($iterations_per_s_total, 0)
Write-Host "Iterations/s:" $iterations_per_s_total
#>
$SqlCmd.CommandText = "select * into os_waits_after from sys.dm_os_wait_stats"
$SqlCmd.ExecuteNonQuery() | Out-Null
$SqlCmd.CommandText = "select * into os_schedulers_after from sys.dm_os_schedulers"
$SqlCmd.ExecuteNonQuery() | Out-Null
<#
$SqlCmd.CommandText = "select * into os_waits from sys.dm_os_wait_stats"
$SqlCmd.ExecuteNonQuery() | Out-Null
#>
$adapter = New-Object System.Data.sqlclient.sqlDataAdapter
$dataset = New-Object System.Data.DataSet
<#
$sql_os_waits =
"select wait_type, wait_time_ms,
signal_wait_time_ms
from os_waits where wait_type = 'SOS_SCHEDULER_YIELD'"
#>
$sql_os_waits =
"select
a.wait_type,
( a.wait_time_ms - b.wait_time_ms )
total_wait_time_ms,
( a.signal_wait_time_ms - b.signal_wait_time_ms )
total_signal_wait_time_ms
from os_waits_after a join os_waits_before b
on a.wait_type = b.wait_type
where a.wait_type = 'SOS_SCHEDULER_YIELD'"
ExecuteSelect -Connection $SqlConnection -Select $sql_os_waits
$sql_os_scheduler =
"select
a.scheduler_id,
( a.total_cpu_usage_ms - b.total_cpu_usage_ms )
total_cpu_usage_ms
from os_schedulers_after a join os_schedulers_before b
on a.scheduler_id = b.scheduler_id
where a.status = 'VISIBLE ONLINE'"
ExecuteSelect -Connection $SqlConnection -Select $sql_os_scheduler
$SqlConnection.Close()