はじめに
C#で型変換に使う主な5つの方法の速さを比べました。
結論
()キャストを使う場合、例外が発生すると極端に遅くなるが、全く例外が発生しないケースでは最速でした。
is, asの型変換はどんなケースでもそれなりの速さが出るようです。
環境
Windows 10 Home, AMD Ryzen 5 3500 6-Core Processor(3.59 GHz), 16GB Memory, Visual Studio 2019, .Net Core 3.1
計測コード
class A
{
public A(string s)
{
member = s;
}
string member;
}
class B
{
public B(string s)
{
member = s;
}
string member;
}
//tryとcatchで囲って()キャストする方法
static void Cast(object[] vs)
{
foreach (object o in vs)
{
try
{
A v = (A)o;
}
catch
{
}
}
}
//asキーワードで変換してからnullチェックする方法
static void AsIf(object[] vs)
{
foreach (object o in vs)
{
A v = o as A;
if (v == null) { }
}
}
//isキーワードで変換できるかをチェックし、そのまま変換する方法
static void IfIs(object[] vs)
{
foreach (object o in vs)
{
if (o is A v) { }
}
}
//isキーワードでチェックしてから()キャストする方法
static void IfIsCast(object[] vs)
{
foreach (object o in vs)
{
if (o is A)
{
A v = (A)o;
}
}
}
//isキーワードでチェックしてからasキーワードで変換する方法
static void IfIsAs(object[] vs)
{
foreach (object o in vs)
{
if (o is A)
{
A v = o as A;
}
}
}
//5つの変換方法を試す
static void TryMethods(int percentageOfA)
{
int separator = (int)(percentageOfA / 100f * ss.Length);
object[] vs = ss.Select((s, i) => i < separator ? (object)new A(s) : new B(s)).ToArray();
Console.WriteLine($"{percentageOfA}%");
Console.WriteLine($"Cast:\t\t{Measure(count, () => Cast(vs))}");
Console.WriteLine($"AsIf:\t\t{Measure(count, () => AsIf(vs))}");
Console.WriteLine($"IfIs:\t\t{Measure(count, () => IfIs(vs))}");
Console.WriteLine($"IfIsCast:\t{Measure(count, () => IfIsCast(vs))}");
Console.WriteLine($"IfIsAs:\t\t{Measure(count, () => IfIsAs(vs))}");
Console.WriteLine();
}
static int count = 3000;
static string[] ss = new string[10000000];
//エントリポイント
public static void Entry()
{
Random random = new Random();
for (int i = 0; i < ss.Length; i++)
{
int length = random.Next(10);
for (int j = 0; j < length; j++)
{
ss[i] += (char)random.Next('A', 'z');
}
}
Console.WriteLine($"count:\t{count}");
Console.WriteLine($"length:\t{ss.Length}");
Console.WriteLine();
TryMethods(0);
TryMethods(20);
TryMethods(40);
TryMethods(60);
TryMethods(80);
TryMethods(100);
}
//関数の処理にかかる時間を計測する
public static TimeSpan Measure(int count, Action action)
{
DateTime time = DateTime.Now;
for (int i = 0; i < count; i++)
{
action();
}
return DateTime.Now - time;
}
結果
##型変換に失敗する参照が含まれている場合
例外処理が重すぎたため、以下の数値設定でひとまず測りました。
count = 1
length = 10000
表の左上の値は型変換に成功する参照が含まれている割合です。
時間の単位は秒です。
0% |
1回目 |
2回目 |
3回目 |
平均 |
Cast |
71.1655179 |
71.8009120 |
71.6555776 |
71.5406692 |
AsIf |
0.0002877 |
0.0003214 |
0.0002501 |
0.0002864 |
IfIs |
0.0002317 |
0.0001495 |
0.0001622 |
0.0001811 |
IfIsCast |
0.0001465 |
0.0001404 |
0.0002372 |
0.0001747 |
IfIsAs |
0.0001366 |
0.0001362 |
0.0001423 |
0.0001384 |
20% |
1回目 |
2回目 |
3回目 |
平均 |
Cast |
56.6513413 |
56.5845480 |
57.0564668 |
56.7641187 |
AsIf |
0.0000406 |
0.0000440 |
0.0000363 |
0.0000403 |
IfIs |
0.0000344 |
0.0000424 |
0.0000406 |
0.0000391 |
IfIsCast |
0.0000363 |
0.0000399 |
0.0000358 |
0.0000373 |
IfIsAs |
0.0000407 |
0.0000351 |
0.0000353 |
0.0000370 |
40% |
1回目 |
2回目 |
3回目 |
平均 |
Cast |
42.7042395 |
42.6855280 |
42.9887469 |
42.7928381 |
AsIf |
0.0000365 |
0.0000365 |
0.0000361 |
0.0000364 |
IfIs |
0.0000364 |
0.0000376 |
0.0000324 |
0.0000355 |
IfIsCast |
0.0000342 |
0.0000369 |
0.0000368 |
0.0000360 |
IfIsAs |
0.0000425 |
0.0000392 |
0.0000337 |
0.0000385 |
60% |
1回目 |
2回目 |
3回目 |
平均 |
Cast |
28.6295313 |
28.4973504 |
28.7604727 |
28.6291181 |
AsIf |
0.0000340 |
0.0000340 |
0.0000343 |
0.0000341 |
IfIs |
0.0000317 |
0.0000315 |
0.0000325 |
0.0000319 |
IfIsCast |
0.0000490 |
0.0000369 |
0.0000372 |
0.0000410 |
IfIsAs |
0.0000315 |
0.0000306 |
0.0000311 |
0.0000311 |
80% |
1回目 |
2回目 |
3回目 |
平均 |
Cast |
14.4235740 |
14.4582490 |
14.4788299 |
14.4535510 |
AsIf |
0.0000315 |
0.0000325 |
0.0000309 |
0.0000316 |
IfIs |
0.0000273 |
0.0000329 |
0.0000315 |
0.0000306 |
IfIsCast |
0.0000304 |
0.0000342 |
0.0000342 |
0.0000329 |
IfIsAs |
0.0000322 |
0.0000363 |
0.0000304 |
0.0000330 |
100% |
1回目 |
2回目 |
3回目 |
平均 |
Cast |
0.0000244 |
0.0000241 |
0.0000264 |
0.0000250 |
AsIf |
0.0000286 |
0.0000272 |
0.0000259 |
0.0000272 |
IfIs |
0.0000263 |
0.0000262 |
0.0000261 |
0.0000262 |
IfIsCast |
0.0000303 |
0.0000358 |
0.0000300 |
0.0000320 |
IfIsAs |
0.0000303 |
0.0000302 |
0.0000301 |
0.0000302 |
型変換に失敗する参照が含まれていない場合
上の結果でいう100%だけを以下の数値設定で測りました。
count = 3000
length = 10000000
100% |
1回目 |
2回目 |
3回目 |
平均 |
Cast |
73.9517405 |
74.0026143 |
74.1104895 |
74.0216148 |
AsIf |
81.3793428 |
81.5171491 |
81.4947477 |
81.4637465 |
IfIs |
81.9090815 |
82.2008835 |
81.9648811 |
82.0249487 |
IfIsCast |
93.9863807 |
94.0810441 |
87.0868509 |
91.7180919 |
IfIsAs |
93.9113493 |
94.2150005 |
87.0688733 |
91.7317410 |
()キャストは最速といっても気持ち速い程度のようです。
IfIsCastとIfIsAsは値が比較的安定していないので、実行状況によって速さが変わりやすいのかもしれません。