4

時間プロパティを含む「アイテム」オブジェクトのコレクションを検索する必要があります。私は迅速な解決策を用意していますが、非常に面倒です(何も改善されない場合は投稿します)。以下は、LINQ などを使用して自分で検索を実行しようとした私の試みです。

私の特定のケースでは、アイテムが時間に基づいて低から高に注文されていることがわかっています。それらを繰り返すと、9/12、9/13、9/14 です。これが注文されていなくても速い解決策を見つけたいのですが、今は重要ではありません。

//ICollection c = GetCollection(); //25,000+ items
DateTime TIME = DateTime.Now.AddDays(-1);

EventLog scan = new EventLog("Application", "Server", "N/A");
EventLogCollection c = scan.Entries;
Console.WriteLine(logs.Count); // All entries already in list here

// 64 sec - SLOW
List<Item> l1 = new List<Item>();
foreach (Item i in c) { 
  if (i.time > TIME) { 
    l1.Add(i); }
}

// 93 sec - SLOWER
var l2 = c.Cast<Item>().AsParallel().Select(n => n.time > TIME);
var i = l2.Count();

// 98 sec - EVEN SLOWER!
List<Item> l3 = new List<Item>();
Parallel.ForEach(c.Cast<Item>(), n => {
    if (n.time > TIME) {
        l3.add(n);
    }
});

私の現在の解決策は、開始時間と終了時間の BinarySearch を実行し、それらのインデックスに基づいて ICollection をループすることです。非常に高速(1〜2秒)ですが、非常に面倒です。別のソリューションは必要ありませんが、パフォーマンスの専門家にこれを投げたいと思いました。

ICollection を検索するためのより高速でエレガントな方法はありますか? ところで、与えられたコレクションを制御することはできず、その構造を変更することはできません。.NET 4.0

いいえ、Windows XP で立ち往生しているため、System.Diagnostics.Eventing.Reader を使用できません。

4

3 に答える 3

2

あなたの「質問」は間違っています。実際には一連のブール値を選択していますが、フィルタリングはしていません。あなたがしたい:

var l2 = c.Cast<EventLogEntry>().AsParallel().Where(n => n.time > TIME);

これは速くはないかもしれませんが、チェックする価値があります。一体、私はそれを並行して行うことさえしません。

実際、Count述語を取るオーバーロードがあるため、これを使用できます。

var count = c.Cast<EventLogEntry>().Count(n => n.time > TIME);

または並列化されたバージョン:

var count = c.Cast<EventLogEntry>().AsParallel().Count(n => n.time > TIME);

Chris のように、実装に 1 分以上かかることにショックを受けています...

于 2012-10-01T13:08:18.930 に答える
1

編集

dasblinkenlight の答えが一般的に正しいという主張について、私は簡単な一般的なヘルパー関数を書きました。

public static int BinaryFirstIndexOf<T>(
    Func<int, T> indexer,
    Func<T, boo> predicate,
    int count)
{
    var low = 0;
    var high = count - 1;

    while (low < (high - 1))
    {
        var mid = low + ((high - low) / 2);

        if (predicate(indexer(mid))
        {
            high = mid;
        }
        else
        {
            low = mid;
        }
    }

    if (predicate(indexer(low)))
    {
        return low;
    }

    if (low != high && predicate(indexer(high)))
    {
        return high;
    }

    return -1;
}

私はこのように使用しますが、

var time = DateTime.Now.AddDays(-1);
var c = scan.Entries;

var first = BinaryFirstIndexOf(
     i => c[i], 
     e => e.TimeGenerated > time,
     c.Count);

if (first >= 0)
{
    var result = new List<Item>(c.Count - first);
    Enumerable.Range(first, c.Count - first).AsParallel()
    .ForAll(i => 
        {
            var j = i - first;
            result[j] = (Item)c[i];
        });
}

こんなことしたくない?

var time = DateTime.Now.AddDays(-1);
var c = scan.Entries;

var cutOff = 0;
for (var i = c.Count - 1; i > - 1; i--)
{
    if (c[i].TimeGenerated < time)
    {
        cutOff = i;
        break;
    }
}

var result = new List<Item>(c.Count - cutOff - 1);
var j = 0;
for (var i = cutOff + 1; i < c.Count; i ++)
{
    result[j] = (Item)c[i];
    j++;
}

必要なデータは最後にあり、コレクションの半分以下しか占めていないと思います。

または、おそらくlinqを使用してキャストを並行して行います。

var time = DateTime.Now.AddDays(-1);
var c = scan.Entries;

var cutOff = 0;
for (var i = c.Count - 1; i > - 1; i--)
{
    if (c[i].TimeGenerated < time)
    {
        cutOff = i;
        break;
    }
}

var result = new List<Item>(c.Count - cutOff - 1);
Enumerable.Range(cutOff + 1, c.Count - cutOff - 1).AsParallel()
    .ForAll(i => 
        {
            var j = i - cutOff - 1;
            result[j] = (Item)c[i];
        });
于 2012-10-01T13:52:37.363 に答える
0

この最初のステートメント:

List<Item> l1 = new List<Item>();
foreach (Item i in c) { 
  if (i.time > TIME) { 
    l1.Add(i); }
}

次のように変更するとパフォーマンスが向上しますか (foreach が実行される前にリストをフィルタリングします):

List<Item> l1 = new List<Item>();
foreach (Item i in c.Where(a => a.time > TIME)) { 
    l1.Add(i);
}
于 2012-10-01T13:15:49.063 に答える