Test-Connectionの最速を目指す

でも、pingでいいじゃない?

そんな! 元も子もないことを!
確かに、PowerShellだろうとBashだろうと「ping.exe」は握手と同じくらいのネットワーク技術者の共通言語、四則計算レベルの常識と言って然るべきです。
でも、あえて、WindowsでPowerShellを使いたい、という欲求もある人もいるんです。そのはずです!
なので、ちょろっと考えてみました。ご一読をどうぞ。

Test-Connection

PowerShellでのping.exeに相当するものは、Test-Connectionです。以下の使い方で構いません。
Test-Connection localhost
だが、試してもらうとわかりますが、これの使い勝手が悪い。というか、遅い。
localhostならまだいいですが、たまに正常に動いていてもすぐCtrl+Cを押したくなるくらいに遅い。
Test-Connectionが遅い理由 ではどこでNetBIOS Name Queryが発行されているのかというと、その原因はTest-Connectionの戻り値にあります。
(中略)
プロパティ内で[System.Net.Dns]::GetHostEntry()メソッドを発行しIPアドレスの逆引きをしていることがわかります。 このメソッドは内部でgethostbyaddr()関数を使用していますのでNetBIOS Name Queryを含めた名前解決*2が実行され、その結果待ちにより処理が遅くなってしまうのです。
Test-Connectionが遅い理由と対策方法について - しばたテックブログ
上記ブログにもありますが、戻り値の IPV4AddressIPV6Address がこういう悪さ?をしているようです。
TypeName   : System.Management.ManagementObject#root\cimv2\Win32_PingStatus
Name       : IPV4Address
MemberType : ScriptProperty
Definition : System.Object IPV4Address {get=$iphost = [System.Net.Dns]::GetHostEntry($this.address)
                         $iphost.AddressList | ?{ $_.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork
              } | select -first 1;}

TypeName   : System.Management.ManagementObject#root\cimv2\Win32_PingStatus
Name       : IPV6Address
MemberType : ScriptProperty
Definition : System.Object IPV6Address {get=$iphost = [System.Net.Dns]::GetHostEntry($this.address)
                         $iphost.AddressList | ?{ $_.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetwork
             V6 } | select -first 1;}
とどのつまり、わざわざ戻り値を返すタイミングで、DNS逆引きをしているからということですね。そりゃ重くなるわけだ。

解決法

というわけで、素早く便利に使うには思わぬ仕様の障害があるわけです。 これの解決法に関しては、上のブログに書いてあるのでそれも参照してみてうださい。
さて、私のオススメとして紹介するのは、"みんな大好き"MSDNに記載されている以下の例示の方法が一番興味深いコードです。
Example 5: Run a test as a background job
This example shows how to run a Test-Connection command as a PowerShell background job.
PowerShell
$job = Start-Job -ScriptBlock { Test-Connection -TargetName (Get-Content "Servers.txt") }
if ($job.JobStateInfo.State -ne "Running") { $Results = Receive-Job $job }
参照元:Test-Connection
要するに、-asjobオプションでバックグラウンドで動かしてあげる方法です。バックグラウンドで動かせば当然戻り値の表示は行いませんから、戻り値のスクリプトも動かないし重くないということです。
ただ、その情報を取得するときに IPV4AddressIPV6Address を表示させては、元の木阿弥ですから、Select-Objectで表示させないよう回避するわけです。

実際のコマンド

では、実際にコマンドを打ってみましょう。
まず前提として、以下のテキストファイル「testfile.txt」があるとします。
192.168.0.1,2,3は疎通でき、4,5は疎通できないとします。
192.168.0.1
192.168.0.2
192.168.0.3
192.168.0.4
192.168.0.5
その上で先のMSDN Exammple.5のコマンドをちょろっと工夫して打ってみましょう。
PS E:\workspace> $job = Test-Connection (Get-Content .\testfile.txt) -asjob -count 1
PS E:\workspace> if ($job.JobStateInfo.State -ne "Running" ){ $result = Receive-Job $job }  else { echo "Running..."}
PS E:\workspace> $result | Select-Object Address, StatusCode

Address       StatusCode
-------       ----------
192.168.0.1           0
192.168.0.2           0
192.168.0.3           0
192.168.0.4       11010
192.168.0.5       11010
0が正常疎通、それ以外は何らかのエラーです。こんな感じですね。
もっとも、結果を出したいだけなら結果だけを表示する -Quiet オプションを使ってあげたほうが楽です。 ただ、うまい具合に組んであげないと、実際に試したIPが分からず、本当にTrue or Falseしか返ってきません。
PS E:\workspace> Test-Connection (Get-Content .\testfile.txt) -Quiet -Count 1
True
True
True
False
False
PS E:\workspace>
PS E:\workspace> Get-Content .\testfile.txt | % { $_ + "," + (Test-Connection $_ -Quiet -Count 1) }
192.168.0.1,True
192.168.0.2,True
192.168.0.3,True
192.168.0.4,False
192.168.0.5,False
Test-Connectionで返ってくる他の情報が欲しい場合には、バックグラウンドで動かしてやってから、取るのがいい方法でしょう。
あるいは、Test-Connectionの結果をもとに、さらにコマンドを動かしたいというのであれば、batで組むよりかは遥かに楽でしょう。

時間測定

最後に、Measure-Commandで時間を調べてみた結果を示します。

確認方法

  • 対象:ローカルネットワークに対する5つのIPアドレス
  • 比較するコマンド内容:
    1. -AsJobオプションによるバックグラウンド実行
    2. -Quietのみの疎通確認実行
      • 具体的なコードは、当ブログ最下部をご参照
  • 実行回数:3回実行
  • 比較対象:3回の実行時間の平均
今回は、コマンドの時間比較が目的ですので、当方の環境は割愛します。当然、他の環境では秒数は変わると思います。ただ、傾向は変わらないことでしょう。

実行結果

実行方法 時間(3回平均)
バックグラウンド実行 3.795 秒
ーQuietのみ 11.807 秒
明確に、-asjobオプションによってバックグラウンドで実行したほうが速いことがわかりました。
当記事は、以上です。これが最速かわかりませんので、 「これが一番早いと思います」 という保険をかけておきます。
以下は、実行結果をそのまま貼り付けています。興味のある方はどうぞ。

参考:実行結果

PS E:\workspace> Measure-Command {
    $job = Test-Connection (Get-Content .\testfile.txt) -asjob -count 1
    While ($job.JobStateInfo.State -eq "Running" ){ 
        ## 何もしない
    }
    $result = Receive-Job $job
    $result | Select-Object Address, StatusCode
}


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 4
Milliseconds      : 6
Ticks             : 40062152
TotalDays         : 4.63682314814815E-05
TotalHours        : 0.00111283755555556
TotalMinutes      : 0.0667702533333333
TotalSeconds      : 4.0062152
TotalMilliseconds : 4006.2152




PS E:\workspace> Measure-Command {
    $job = Test-Connection (Get-Content .\testfile.txt) -asjob -count 1
    While ($job.JobStateInfo.State -eq "Running" ){ 
        ## 何もしない
    }
    $result = Receive-Job $job
    $result | Select-Object Address, StatusCode
}


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 3
Milliseconds      : 706
Ticks             : 37062980
TotalDays         : 4.28969675925926E-05
TotalHours        : 0.00102952722222222
TotalMinutes      : 0.0617716333333333
TotalSeconds      : 3.706298
TotalMilliseconds : 3706.298




PS E:\workspace> Measure-Command {
    $job = Test-Connection (Get-Content .\testfile.txt) -asjob -count 1
    While ($job.JobStateInfo.State -eq "Running" ){ 
        ## 何もしない
    }
    $result = Receive-Job $job
    $result | Select-Object Address, StatusCode
}


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 3
Milliseconds      : 675
Ticks             : 36750682
TotalDays         : 4.25355115740741E-05
TotalHours        : 0.00102085227777778
TotalMinutes      : 0.0612511366666667
TotalSeconds      : 3.6750682
TotalMilliseconds : 3675.0682




PS E:\workspace> Measure-Command {
    Test-Connection (Get-Content .\testfile.txt) -Quiet -Count 1
}


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 11
Milliseconds      : 776
Ticks             : 117768934
TotalDays         : 0.000136306636574074
TotalHours        : 0.00327135927777778
TotalMinutes      : 0.196281556666667
TotalSeconds      : 11.7768934
TotalMilliseconds : 11776.8934




PS E:\workspace> Measure-Command {
    Test-Connection (Get-Content .\testfile.txt) -Quiet -Count 1
}


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 11
Milliseconds      : 916
Ticks             : 119163149
TotalDays         : 0.000137920311342593
TotalHours        : 0.00331008747222222
TotalMinutes      : 0.198605248333333
TotalSeconds      : 11.9163149
TotalMilliseconds : 11916.3149




PS E:\workspace> Measure-Command {
    Test-Connection (Get-Content .\testfile.txt) -Quiet -Count 1
}


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 11
Milliseconds      : 729
Ticks             : 117294046
TotalDays         : 0.000135756997685185
TotalHours        : 0.00325816794444444
TotalMinutes      : 0.195490076666667
TotalSeconds      : 11.7294046
TotalMilliseconds : 11729.4046




PS E:\workspace> 

コメント

このブログの人気の投稿

リモートワークをLogicoolのマウスとキーボードで複数PC切り替えて優勝した

VBAでのInterfaceやキャスト

SUPERHOTがいかにSUPERHOTか語りたい