1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[C#] 参照型における()キャストとis, asキーワードによる型変換の速さの違い

Last updated at Posted at 2021-01-22

はじめに

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は値が比較的安定していないので、実行状況によって速さが変わりやすいのかもしれません。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?