Ho una lista di 500000 generate casualmente Tuple<long,long,string>
oggetti su cui sto effettuando un semplice "tra" ricerca:Perché l'elaborazione di una matrice ordinata è più lenta di una matrice non ordinata?
var data = new List<Tuple<long,long,string>>(500000);
...
var cnt = data.Count(t => t.Item1 <= x && t.Item2 >= x);
Quando ho generare il mio allineamento casuale e corro la mia ricerca di 100 valori generati casualmente di x
, il ricerche completate in circa quattro secondi. Conoscendo il numero great wonders that sorting does to searching, tuttavia, ho deciso di ordinare i miei dati - prima con Item1
, poi con Item2
e infine con Item3
- prima di eseguire le mie 100 ricerche. Mi aspettavo che la versione ordinata si svolgesse un po 'più velocemente a causa della previsione del ramo: ho pensato che una volta arrivati al punto in cui Item1 == x
, tutti gli ulteriori controlli di t.Item1 <= x
avrebbero previsto correttamente il ramo come "non prendere", accelerando la porzione di coda della ricerca. Con mia grande sorpresa, le ricerche sono durate il doppio su un array ordinato!
Ho provato a cambiare l'ordine in cui eseguivo i miei esperimenti e ho utilizzato seme diverso per il generatore di numeri casuali, ma l'effetto è stato lo stesso: le ricerche in un array non ordinato sono quasi il doppio delle ricerche nel stesso array, ma ordinato!
Qualcuno ha una buona spiegazione di questo strano effetto? Segue il codice sorgente dei miei test; Sto usando .NET 4.0.
private const int TotalCount = 500000;
private const int TotalQueries = 100;
private static long NextLong(Random r) {
var data = new byte[8];
r.NextBytes(data);
return BitConverter.ToInt64(data, 0);
}
private class TupleComparer : IComparer<Tuple<long,long,string>> {
public int Compare(Tuple<long,long,string> x, Tuple<long,long,string> y) {
var res = x.Item1.CompareTo(y.Item1);
if (res != 0) return res;
res = x.Item2.CompareTo(y.Item2);
return (res != 0) ? res : String.CompareOrdinal(x.Item3, y.Item3);
}
}
static void Test(bool doSort) {
var data = new List<Tuple<long,long,string>>(TotalCount);
var random = new Random(1000000007);
var sw = new Stopwatch();
sw.Start();
for (var i = 0 ; i != TotalCount ; i++) {
var a = NextLong(random);
var b = NextLong(random);
if (a > b) {
var tmp = a;
a = b;
b = tmp;
}
var s = string.Format("{0}-{1}", a, b);
data.Add(Tuple.Create(a, b, s));
}
sw.Stop();
if (doSort) {
data.Sort(new TupleComparer());
}
Console.WriteLine("Populated in {0}", sw.Elapsed);
sw.Reset();
var total = 0L;
sw.Start();
for (var i = 0 ; i != TotalQueries ; i++) {
var x = NextLong(random);
var cnt = data.Count(t => t.Item1 <= x && t.Item2 >= x);
total += cnt;
}
sw.Stop();
Console.WriteLine("Found {0} matches in {1} ({2})", total, sw.Elapsed, doSort ? "Sorted" : "Unsorted");
}
static void Main() {
Test(false);
Test(true);
Test(false);
Test(true);
}
Populated in 00:00:01.3176257
Found 15614281 matches in 00:00:04.2463478 (Unsorted)
Populated in 00:00:01.3345087
Found 15614281 matches in 00:00:08.5393730 (Sorted)
Populated in 00:00:01.3665681
Found 15614281 matches in 00:00:04.1796578 (Unsorted)
Populated in 00:00:01.3326378
Found 15614281 matches in 00:00:08.6027886 (Sorted)
A causa della previsione del ramo: p –
@jalf, mi aspettavo che la versione ordinata eseguisse un po 'più veloce a causa della previsione del ramo. Pensavo che una volta arrivati al punto in cui 'Item1 == x', tutti gli ulteriori controlli di' t.Item1 <= x' avrebbero previsto il branch correttamente come "no take", accelerando la porzione di coda della ricerca. Ovviamente, questa linea di pensiero è stata smentita dalla dura realtà :) – dasblinkenlight
È interessante notare che, per 'TotalCount' attorno a' 10.000' o meno, la versione ordinata ha prestazioni più veloci (ovviamente, banalmente più veloci con quei piccoli numeri) (FYI, il tuo codice potrebbe voler avere la dimensione iniziale di 'var data List = new List> (500000)' associato a 'TotalCount' invece di codificare la capacità) –