今日は、.NET のクラスライブラリ設計の本を読んだ。Collection 周りが理解できていないのがわかったので、設計方針をリストアップして、分かっていないところをつぶしたいと思う。
配列
DO: パブリックなAPIにおいては、配列よりもコレクションの使用を選択する
public Collection<Item> Items { get; set; }
コレクションのほうが、高いユーザビリティを誇るため。ただし、低レベルのAPIを開発しているときは、配列を使用するほうが良いときがある。配列の要素のアクセスは、コレクションより高速であるから。メモリを抑えて高速化したいときなど。
また、バイトの場合は、コレクションより配列。(これは上記の理由と思われる。)
public byte[] ReadBytes() {...
多次元配列の代わりに、ジャグ配列を使用する
ジャグ配列というのは、配列の要素が配列であるもの。多次元配列と比較すると、メモリ消費量が有利で、CLRは、ジャグ配列におけるインデックス操作も最適化してくれるので、早そうです。これは、本当かしらんので試してみましょう。
メモリがどう違うか確かめてみます。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CollectionSpike
{
class NewInt
{
public int Value { get; private set; }
public NewInt(int value)
{
Value = value;
}
}
class OldInt
{
public int Value { get; private set; }
public OldInt(int value)
{
Value = value;
}
}
class ArraySpike
{
public void Exec()
{
OldInt[][] jaggedArray =
{
new OldInt[] {new OldInt(1), new OldInt(2), new OldInt(3), new OldInt(4)},
new OldInt[] {new OldInt(5), new OldInt(6), new OldInt(7)},
new OldInt[] {new OldInt(8)},
new OldInt[] {new OldInt(9)}
};
NewInt[,] multiDimArray =
{
{new NewInt(1), new NewInt(2), new NewInt(3), new NewInt(4)},
{new NewInt(5), new NewInt(6), new NewInt(7), new NewInt(0)},
{new NewInt(8), new NewInt(0), new NewInt(0), new NewInt(0)},
{new NewInt(9), new NewInt(0), new NewInt(0), new NewInt(0)}
};
var sw = new System.Diagnostics.Stopwatch();
var times = 10000000;
sw.Start();
for (int i = 0; i < times; i++)
{
foreach (var items in jaggedArray)
{
foreach (var item in items)
{
if (item.Value == 8)
{
}
}
}
}
sw.Stop();
Console.WriteLine($"jagged.foreach.foreach: {sw.Elapsed}");
sw.Restart();
for (int i = 0; i < times; i++)
{
jaggedArray.SelectMany(x => x).Select(x => x.Value % 2 == 0);
}
sw.Stop();
Console.WriteLine($"jagged.SelectMany.Select: {sw.Elapsed}");
sw.Restart();
for (int i = 0; i < times; i++)
{
foreach (var item in multiDimArray)
{
if (item.Value == 8)
{
}
}
}
sw.Stop();
Console.WriteLine($"multiDim foreach: {sw.Elapsed}");
Console.WriteLine($"jagged: {jaggedArray} multi: {multiDimArray}");
}
}
}
実行結果
jagged.foreach.foreach: 00:00:03.2893552
jagged.SelectMany.Select: 00:00:01.8337845
multiDim foreach: 00:00:09.8060043
ん、確かに。しかし、Linq が一番早いのは意外!優秀ですね。ちなみに、多次元配列だと、Linqはつかえませんでした。
じゃあ、メモリ。Visual Studio で、ヒープのスナップショットを撮って、比較してみます。ジャグ配列は単純に、ある型としてヒープが消費されていますが、多次元配列は、新たに多次元配列の型が出来てメモリを消費しているようです。たしかに、倍ぐらい多次元配列のほうがメモリを食っています。今はLinq もあるし、確かにジャグ配列ですな。
長くなったので、まずは第一弾ここまで。