VB.NETでReDim Preserveを使うくらいなら配列を使うのをやめたら?

はじめに

弊社のブログで現在一番アクセス数が多いのは(個人的には意外ですが)ReDimの記事です。

jyuch.hatenablog.com

この記事でもReDim Preserveには否定的な感じで書いていますが、現在でもこの意見は変わりません。

今北産業

  • 添え字操作がバグの温床になる
  • 配列の使用意図を読み取るのに文脈に読み解かないといけないから可読性がマッハで低下する
  • マジで気を付けないとパフォーマンス的にデメリットになるし、気を付けられる人はそもそもVBつkにゃーん

⇒定数として複数の値を定義したり、配列しか受け入れないメソッドへの引数で使う以外でVBで配列とReDim Preserveを使う価値はない

How about ReDim?

ReDimってどういう仕組みで配列の拡張または縮小を行っているのか確認してみましょう。

Dim a As String() = New String() {"Hello", "World"}
ReDim Preserve a(2)

のコードは以下のコードと等価です。

Dim a As String() = New String() {"Hello", "World"}
Dim b(2) As String

For i As Integer = 0 To a.GetLength(0)
    b(i) = a(i)
Next

a = b

単純に言ってしまうと、新しいサイズの配列を生成して値をコピーしているだけです。

ReDim Preserveに否定的な理由

そんじゃ、なんで弊社がReDim Preserve(というか配列そのもの)に否定的な意見*1なのかと言いますと

配列の添え字の制御が面倒(かつバグの温床になりやすい)

ReDim Preserveを使う場面って最初からデータの数が正確に予想できない場合がほとんどだと思います。

例えば以下のコードのように入力をバッファする場合、正確な数が予想できないため必要に応じて配列を拡張する必要があります。

Dim currentIndex = 0
Dim buffer(4) As String

While True
    Dim input = Console.ReadLine()

    If input = "q" Then
        Exit While
    End If

    If currentIndex > buffer.GetLength(0) Then
        ReDim Preserve buffer(buffer.GetLength(0) + 5)
    End If

    buffer(currentIndex) = input
    currentIndex += 1
End While

For Each it In buffer
    If it <> Nothing Then
        Console.WriteLine(it)
    End If
Next

入力された値をバッファし、最後に一気に出力しています。

それで、こちらがQueue(Of T)を使ったバージョン。

Dim buffer As New Queue(Of String)

While True
    Dim input = Console.ReadLine()

    If input = "q" Then
        Exit While
    End If

    buffer.Enqueue(input)
End While

For Each it In buffer
    Console.WriteLine(it)
Next

余計な変数が1つ消え、めんどくさい添え字計算も省略できています。*2

VBの主戦場たる生産性アプリケーションでは基本的にそこまでクリティカルな性能が求められることはなく*3、むしろコードでどこまでドメインを表現できるか・保守性の高いコードであるかが求められると考えています。

後述しますが、そのコードでのドメインの表現・保守性を考えると、配列の添え字を操作するよりも適切なコレクションクラスを用いてドメインのセマンティクスをコード上で直接表現したほうが保守性が高くなるのではないでしょうか。

使用意図が分かりずらい

添え字計算を駆使すれば、配列はキュー*4・スタック・リングバッファー等々のセマンティクスを表現できます。

表現できるのはいいのですが、その駆使された添え字計算について後から読まなくてはならないあなたの後任の事も考えてあげましょう。

つまるところ、ぐちゃぐちゃな添え字計算を読み取って使用意図をくみ取るのと、専用のコレクションが使われているのを読み取るのでは、将来あなたの後任にとってどちらが楽ですか?という事です。

多用するとパフォーマンス的に不利になりやすい

上でも述べましたが、ReDim Preserveは新しい領域を確保して値をコピーした後に古い領域をガベージコレクタにブン投げます。

特に配列のサイズが大きい*5ケースでReDim Preserveを多用するとメモリアロケーションガベージコレクションが多発してパフォーマンスに明らかな悪影響を及ぼします。

代替手段

ほとんどのケースでは

docs.microsoft.com

の中から適したコレクションクラスを選択するだけで要求を満足するはずです。

(古い記事では特に)配列の代替としてArrayListを推しているケースが多いですが、パフォーマンス・型安全の点からみてもジェネリックコレクションと比べたメリットが無いので2秒でゴミ箱にぶち込みましょう。

おわりに

  1. ジェネリックコレクションを知らないのなら、まずはそれを勉強しろ
  2. パフォーマンスを重要視したいのはわかるが、お前のアプリケーションでそこまでの最適化が本当に必要か胸に手を当てて考えてみろ
  3. そうでないならVB.NETでReDim Preserveを使うくらいなら配列を使うのをやめたら?

*1:ただし生産性アプリケーションの開発に限る

*2:ちなみに、配列版は実はバグがあります。探してみましょう!!

*3:もちろん速いに越したことはありませんが、求められる速度はプリミティブなメモリ操作が必要になるほどではないと考えています

*4:上のコードの様に

*5:具体的にいうと配列のサイズが86キロバイトを超えるケース。64ビットで動作していると仮定すると、文字列の配列で要素数が11008を超えるような

Windowsの記憶域に他から引っ張ってきたSSDを追加したい

はじめに

弊社ではVMware Workstationで検証環境を構築しており、その仮想マシンの格納用にSSDをシンプル構成(いわゆるRAID0)の記憶域(Windowsでのストレージプール)を構成しています。

記憶域用に新品を買ってくるのではなく、他で余ったSSDが出たら都度交換・追加をしているのですが単純にケーブルを繋いでも記憶域に追加できないのでそれについてです。

たまにしかやらないので都度ググっていたのが、面倒になったので自分用にメモします。

既存パーテーションの削除

まずは既存パーテーションを削除します。普通のパーテーションならディスクの管理から一撃なのですが、EFIパーテーションはディスクの管理から消せないのでdiskpartで消し去る必要があります。*1

PS F:\> diskpart.exe

Microsoft DiskPart バージョン 10.0.17763.1

Copyright (C) Microsoft Corporation.
コンピューター: ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓

DISKPART> list disk

  ディスク      状態           サイズ   空き   ダイナ GPT
  ###                                          ミック
  ------------  -------------  -------  -------  ---  ---
  ディスク 0    オンライン           476 GB      0 B        *
  ディスク 1    オンライン          1863 GB      0 B        *
  ディスク 4    オンライン          1024 GB      0 B        *
  ディスク 5    オンライン           447 GB   446 GB        *

DISKPART> select disk 5

ディスク 5 が選択されました。

DISKPART> list partition

  Partition ###  Type                Size     Offset
  -------------  ------------------  -------  -------
  Partition 1    システム               200 MB    20 KB

DISKPART> select partition 1

パーティション 1 が選択されました。

DISKPART> delete partition override

DiskPart は選択されたパーティションを正常に削除しました。

DISKPART> exit

DiskPart を終了しています...

nasunoblog.blogspot.com

Can PoolフラグをTrueにする

多分誤ってプールに追加しないように一度使用したディスクはCan PoolフラグにFalseが設定されます。

そのため、Can Poolフラグをリセットする必要があります。

PS F:\> Get-PhysicalDisk

DeviceId FriendlyName         SerialNumber                             MediaType CanPool OperationalStatus HealthStatus
-------- ------------         ------------                             --------- ------- ----------------- ------------
2        SanDisk ▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓                             SSD       False   OK                Healthy
3        INTEL ▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                       SSD       False   OK                Healthy
5        SanDisk ▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓                             SSD       False   OK                Healthy
1        TOSHIBA ▓▓▓▓▓▓▓▓▓▓   ▓▓▓▓▓▓▓▓▓                                HDD       False   OK                Healthy
0        INTEL ▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ SSD       False   OK                Healthy

PS F:\> Get-PhysicalDisk -SerialNumber "▓▓▓▓▓▓▓▓▓▓▓▓"

DeviceId FriendlyName         SerialNumber MediaType CanPool OperationalStatus HealthStatus Usage            Size
-------- ------------         ------------ --------- ------- ----------------- ------------ -----            ----
5        SanDisk ▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓ SSD       False   OK                Healthy      Auto-Select 447.13 GB

PS F:\> Get-PhysicalDisk -SerialNumber "▓▓▓▓▓▓▓▓▓▓▓▓" | Reset-PhysicalDisk

PS F:\> Get-PhysicalDisk

DeviceId FriendlyName         SerialNumber                             MediaType CanPool OperationalStatus HealthStatus
-------- ------------         ------------                             --------- ------- ----------------- ------------
2        SanDisk ▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓                             SSD       False   OK                Healthy
3        INTEL ▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓                       SSD       False   OK                Healthy
5        SanDisk ▓▓▓▓▓▓▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓                             SSD       True    OK                Healthy
1        TOSHIBA ▓▓▓▓▓▓▓▓▓▓   ▓▓▓▓▓▓▓▓▓                                HDD       False   OK                Healthy
0        INTEL ▓▓▓▓▓▓▓▓▓▓▓▓▓  ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓ SSD       False   OK                Healthy

signal-flag-z.blogspot.com

あとはコントロールパネルからディスクを記憶域に追加すればOKです。

おわり

*1:消す必要があるのかはしらん

WSUSで設定する分類ってなんじゃらほい

はじめに

Windows Serverは投げ捨てるもの

完全に自分用のメモです。

検証環境の構築でWindows Serverのセットアップを何度もやっているのですが、管理とかアップデートの適用がいい加減面倒になってきたのでAD + WSUSの環境を構築しました。

WSUSは更新の対象とする製品と分類をあらかじめ設定するのですが、その分類の中身がよくわからなかったので、それについてです。

blogs.technet.microsoft.com

こんな感じだそうです。