試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

リストへのデータ追加にかかる方向/時間のテストをC#言語に移植

GLibで連結リスト(リスト構造)にデータを追加する方向とその時間について」のテストをC#言語に移植してMono上で実行してみた。

メモ

  • GLibのGTimerに相当する機能*1はC#ではSystem.Diagnostics.Stopwatchオブジェクトとなり、使い方も似ているが、経過時間の形(取り出し方や単位・形式)や一度時間の計測を止めた後の再開(リセットされるかどうか)に関する挙動などは異なるので注意。
  • リストはSystem.Collections.Generic.Listオブジェクトにて山括弧に中のデータ型の名前を指定して使用する(「List<int>」や「List<String>」のような形)。末尾にデータを追加するメンバ関数Add()だが、先頭に追加するものはないので、先頭へデータを追加する処理ではInsert()で0番に追加する形をとった。

データを逆順にして手前から追加するものと末尾にそのまま追加するものとを比較するコード

[任意]ファイル名: ListBench.cs

using System.Collections.Generic;  // List
using System.Diagnostics;          // Stopwatch
using System;

/*
 * リスト構造へデータを追加するのにかかる時間を
 * 先頭に追加するのと末尾に追加するのとでそれぞれ測定する
 *
 * 先頭に追加するバージョン:
 * gmcs -d:PREPEND ListBench.cs -out:ListBenchPrepend.exe
 * 末尾に追加するバージョン:
 * gmcs -d:APPEND ListBench.cs -out:ListBenchAppend.exe
 * それぞれ-d:VERBOSEを付けると各データを表示する
 * gmcs -d:VERBOSE,PREPEND ListBench.cs -out:ListBenchPrependVerbose.exe
 * gmcs -d:VERBOSE,APPEND ListBench.cs -out:ListBenchAppendVerbose.exe
 */

#if PREPEND
# if APPEND
#  error "PREPENDとAPPENDの両方を定義することはできません"
# endif
#else
# if APPEND
# else
#  error "PREPENDとAPPENDのいずれかを定義する必要があります"
# endif
#endif

namespace ListBench
{
  class MainClass
  {
    public const int cnt = 1000;
#if VERBOSE
    public static void DisplayData (int data)
    {
      Console.WriteLine (data);
    }
#endif
    public static int Main (string[] args)
    {
      List<int> list = new List<int> ();  /* 山括弧で型指定・内容は空に初期化される */
      /* 開始 */
      Stopwatch sw = Stopwatch.StartNew ();  /* タイマー生成と同時に計測開始 */
#if PREPEND
      /* 先頭に追加する場合・データは逆順に入れる */
      for (int i = cnt - 1; i > -1; i--)
        list.Insert (0, i);
#else
# if APPEND
      /* 末尾に追加する場合 */
      for (int i = 0; i < cnt; i++)
        list.Add (i);
# endif
#endif
      /* 停止 */
      /* 停止後の再開はStart()、最初から開始し直すにはReset()後Start() */
      sw.Stop ();
#if VERBOSE
      /* 内容を表示 */
      list.ForEach (DisplayData);
#endif
      /* 計測した経過時間を表示 */
      /* Elapsed,ElapsedMilliseconds,ElapsedTicks(静的メンバStopwatch.Frequencyで割る)から選択 */
      Console.WriteLine ("elapsed time: {0}", ((double) sw.ElapsedTicks / (double) Stopwatch.Frequency));
      return 0;
    }
  }
}

データをそのままの順番で追加後リストを逆順にするものと末尾にそのまま追加するものとを比較するコード

[任意]ファイル名: ListBench2.cs

using System.Collections.Generic;  // List
using System.Diagnostics;          // Stopwatch
using System;

/*
 * リスト構造へデータを追加するのにかかる時間を
 * 先頭に追加するのと末尾に追加するのとでそれぞれ測定する
 *
 * 先頭に追加するバージョン:
 * gmcs -d:PREPEND ListBench2.cs -out:ListBench2Prepend.exe
 * 末尾に追加するバージョン:
 * gmcs -d:APPEND ListBench2.cs -out:ListBench2Append.exe
 * それぞれ-d:VERBOSEを付けると各データを表示する
 * gmcs -d:VERBOSE,PREPEND ListBench2.cs -out:ListBench2PrependVerbose.exe
 * gmcs -d:VERBOSE,APPEND ListBench2.cs -out:ListBench2AppendVerbose.exe
 */

#if PREPEND
# if APPEND
#  error "PREPENDとAPPENDの両方を定義することはできません"
# endif
#else
# if APPEND
# else
#  error "PREPENDとAPPENDのいずれかを定義する必要があります"
# endif
#endif

namespace ListBench2
{
  class MainClass
  {
    public const int cnt = 1000;
#if VERBOSE
    public static void DisplayData (int data)
    {
      Console.WriteLine (data);
    }
#endif
    public static int Main (string[] args)
    {
      List<int> list = new List<int> ();  /* 山括弧で型指定・内容は空に初期化される */
      /* 開始 */
      Stopwatch sw = Stopwatch.StartNew ();  /* タイマー生成と同時に計測開始 */
#if PREPEND
      /* 先頭に追加する場合・データは逆順に入れる */
      for (int i = 0; i < cnt; i++)  /* 本来の順番で入れるものとする */
        list.Insert (0, i);
      /* 「逆順の逆順」にして方向を戻す */
      list.Reverse ();
#else
# if APPEND
      /* 末尾に追加する場合 */
      for (int i = 0; i < cnt; i++)
        list.Add (i);
# endif
#endif
      /* 停止 */
      /* 停止後の再開はStart()、最初から開始し直すにはReset()後Start() */
      sw.Stop ();
#if VERBOSE
      /* 内容を表示 */
      list.ForEach (DisplayData);
#endif
      /* 計測した経過時間を表示 */
      /* Elapsed,ElapsedMilliseconds,ElapsedTicks(静的メンバStopwatch.Frequencyで割る)から選択 */
      Console.WriteLine ("elapsed time: {0}", ((double) sw.ElapsedTicks / (double) Stopwatch.Frequency));
      return 0;
    }
  }
}

結果

意外なことにAdd()により末尾にデータを追加したほうが高速となった。原因としては「末尾に追加するAdd()のほうが内部的にはリストの先頭から入れている」などが考えられるが、よくは分からない。
Wine上の.NET Framework上でもやはり0番にInsert()するのよりAdd()で後ろに追加したほうが高速となった。

関連記事:

使用したバージョン:

  • Mono 2.2

*1:コード内に指定した開始位置と終了位置の間における処理時間の計測