[PowerShell]メモリ負荷テスト用スクリプト

こんにちは！れーあです。
今回はメモリに負荷をかけたいときに使えるスクリプトを作っていきます。

それではどうぞ。

## 前提情報
<b> スクリプト実行環境 </b>

```
PS C:\Users\user> $PSVersionTable.PSVersion

Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      19041  4170
```

## スクリプト内容
今回のスクリプトは下記です。
Read-Hostにより1～100%の使用率を決定した後、確実に指定値以上になるように「+1」をした値を格納します。

```Memory_Usage_Limit.ps1
# 目標メモリ使用率を指定
$ReadMemUsagePct = Read-Host "Target Memory Usage Percentage ? [1-100]"
$targetMemUsagePct = [int64]$ReadMemUsagePct + 1

# 開始メッセージ出力
Write-Host "Memory Usage Limit Test:" (Get-Date) "Start."

# 総メモリサイズ取得
$totalMem = [int64](Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory

# メモリ使用率計算関数の定義
function Get-MemUsagePct {
	$availableMem = [int64]((Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory * 1024)
	$currentUsage = $totalMem - $availableMem
	return ($currentUsage / $totalMem) * 100
}

# 現在メモリ使用率取得
$currentUsagePct = Get-MemUsagePct

# 配列作成
$arrayList = @()
$MemGBSize = [int64]($totalMem / [math]::Pow(1024,3))
$chunkSize = [math]::Floor(1GB / $MemGBSize)

# 配列追加用値比較 (指定最小値, 現在メモリ使用サイズ)
while ($currentUsagePct -lt $targetMemUsagePct){
	$addMemBytes = [math]::Round($totalMem * ($targetMemUsagePct / 100)) - ($totalMem * ($currentUsagePct / 100))
	$currentChunkSize = [math]::Floor([math]::Min([int64]$chunkSize, [int64]$addMemBytes) / $MemGBSize)

	# 配列追加
	$intArray = [Decimal[]]::New($currentChunkSize)
	for ($i = 0; $i -lt $intArray.Length; $i++){
		$intArray[$i] = 0
	}
	$arrayList += , $intArray

	# メモリ使用率取得
	$currentUsagePct = Get-MemUsagePct
}

# メモリ解放
Read-Host "Memory relase. Please Press Enter Key."
$arrayList = $null
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
[GC]::Collect()

# 終了メッセージ出力
Write-Host "Memory Limit Test." (Get-Date) "End."
```

## 気になる箇所
今回配列を作成して、配列をデータで埋めてセッションを保持し続けることでメモリの高負荷状態を維持するという内容です。

・Decimal型の利用
配列追加の際、「Decimal型」を使用するのはこの型がもっとも多くの値を格納することができるからです。
当初、他の型を利用して作成をしていたのですが、ここの型によってメモリの目標使用率までの上昇率が変化し、少ない値しか入れられない型ほど、処理に時間を要するという事象を確認したため、可能な限り最大の値を入れられる「Decimal型」を採用しました。

・配列追加内のfor文
配列追加の部分で「＄intArray[＄i] = 0」を入れている理由は、
配列の中に確実に値を入れて使用できるデータを最大限利用するためです。
この処理を入れない場合、配列追加の処理の途中で次のループに
行ってしまい、メモリの使用率が十分に上がり切りません。

・メモリ解放の[GC]
処理の最後に[GC]を使用した3行があります。これは、メモリを確実に解放するために記載しています。
この記載がない場合はまれにセッションが残ったままとなり、メモリの使用率が高いままとなる場合があります。
</br>


## 実際に動かしてみた
それでは実際に動かしてみます。
まず、スクリプトを実行する前に、タスクマネージャーを開いて現在のメモリ使用率を確認します。

私のPCではスクリプト実行前はメモリ使用率が「52%」であることが
確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2632150/cfc32500-9055-1dd5-c62f-3df0a967bcc3.png)

次にPowerShellを「管理者として実行」で開きます。
そしてスクリプトを実行します。
実行すると目標メモリ使用率を聞かれるので、任意の値を入力後、「Enterキー」を押下しましょう。今回は「80%」とします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2632150/8f564a53-37e8-64f9-a496-4d0218e86481.png)

目標値まで使用率が上がるとEnterキーを押すよう催促されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2632150/ad49b2d4-7748-410f-0d97-ca09cbc38478.png)

この時、Enterキーを押す前にタスクマネージャーからメモリの使用率を
みてみると、目標値である「80% + 1%」である「81%」となっている
ことが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2632150/09c2ef47-eb3d-081a-96a7-28376e400f34.png)
</br>
それでは「Enterキー」を押下して、メモリを解放しましょう。
押下後、スクリプトが終了します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2632150/ce113061-2a5a-bbb3-36c3-987f6735398f.png)
</br>

再度タスクマネージャーを開くと、スクリプト実行前のメモリ利用率前後まで値が戻っています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2632150/80915b59-a0da-66f7-9006-e715875be2bf.png)


## おわりに
というわけで以上です。
WindowsServerを利用してリソースの負荷テストを行うことは結構あると思っていて、メモリの負荷をどうにか自動で上げることはできないかと思い今回のスクリプトを作成しました。

メモリの目標使用率はRead-Hostで入力するようにしていますが、確実に値が決まっているのであれば、
「＄ReadMemUsagePct」、「＄targetMemUsagePct」に直接値を
記載してもよいと思います。

また、今回は処理の中に入れていませんが、誤った値が入力された際、警告する処理を入れてみるとスクリプトとしての精度が上がっていいなぁと思います。

1点留意事項としては、メモリ使用率に「100%」を指定した場合、
当然ながら、ハングアウトする可能性があるため利用の際は注意してください。
最大でも「90%」程度に抑えると無難かと思います。

ぜひ、同じようなテストがあれば参考にしてみてください。

それではまた。

## 参考
https://learn.microsoft.com/ja-jp/powershell/scripting/samples/collecting-information-about-computers?view=powershell-5.1

https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_numeric_literals?view=powershell-5.1

https://yantzn.com/entry/powershell-variables/

https://learn.microsoft.com/ja-jp/powershell/scripting/learn/deep-dives/everything-about-arrays?view=powershell-5.1

https://learn.microsoft.com/ja-jp/dotnet/api/system.gc.waitforpendingfinalizers?view=net-8.0

https://learn.microsoft.com/ja-jp/dotnet/api/system.gc.collect?view=net-8.0