Brainer: nu med .NET

man jul 05, 2010 (Rolf)

Den sidste brainer vi havde med med Verilog, blev løst rimelig hurtigt, selv om Morten Piil er mere til C og C++ end til Verilog. Godt gået!

Nu prøver vi så en fra softwarens verden, hvor vi i et nyligt projekt er kommet rigtig godt ind i at bruge .NET på en embedded platform. Det kan gøres nogenlunde fornuftigt, med det som kaldes “Compact Framework” (hvor compact vel egentlig bare betyder, at pladsforbruget ikke går fuldstændig amok). :-)

ArrayList al = new ArrayList(string_array);

foreach (MyOwnType mot in al) { // do something simple; }

Koden er enkel og skrevet op helt generisk – men i den her sammenhæng er der måske 5x hastighedsforbedring ved at omskrive koden. Meget nemt, når man lige ved det.

Hvad skal man gøre? Og hvordan kan performance i sådan en enkel konstruktion være så dårlig?

Første med et rigtig svar i kommentarfeltet vinder hele hæderen.

UPDATE: Løsningen kommer her – den var nok en tand for svær .-)

Kommentarer (8)

  1. Mikkel Elmholdt added on 5. juli 2010

    Brug List i stedet for ArrayList:

    List motList = new List();

    foreach (MyOwnType mot in motList) { // do something simple; }

  2. Mikkel Elmholdt added on 5. juli 2010

    OK, jeres kommentar-software åd mine kant-paranteser så første linine i kodeeksemplet ser underligt ud. Det skulle have været:

    List[MyOwnType] motList = new List[MyOwnType]()

    hvor “[" og "]” erstattes af kantparanteser.

  3. Erik Haggstrom added on 5. juli 2010

    med foreach satsen skapar man en instans av klassen IEnumerator som sedan anvands for att ga igenom listan. Varje objekt i listan maste konverteras till MyOwnType. Detta gors med en try catch sats runt som kan ta hand om exceptions vid konverteringen och returnera null, samma som t= “object” o as MyOwnType. Exceptions tar lang tid och att skapa IEnumerator objekt tar langt tid.

    // foreach(MyOwnType mot in al) {}
    IEnumerator e=al.GetEnumerator();
    while(e.GetNext()) {
    //MyOwnType mot = e.Current as MyOwnType;
    MyOwnType mot;
    try {
    mot = (MyOwnType)e.Current;
    } catch (Exception) {
    mot = null;
    }
    // do something simple;
    }

    .NET ar kanske inte det basta alternativet nar det galler embedded.

  4. Rolf added on 9. juli 2010

    Der er stadig plads til et godt svar, som kommer helt ind til kernen :-)

    Compact Framework er en interessant størrelse.

  5. David Carlsson added on 11. juli 2010

    En vigtig ting er at predefinere sin ArrayList, for hvis der kommer flere elementer i end default størrelsen, som er 4, så tager auto expansion over og det skaber overhead. I dette eksempel så skal man bruge FOR i stedet for FOREACH, fordi at FOREACH involverer virtual method og det skaber også meget overhead.

  6. Cristy added on 19. juli 2010

    med foreach satsen skapar man en instans av klassen IEnumerator som sedan anvands for att ga igenom listan. Varje objekt i listan maste konverteras till MyOwnType. Detta gors med en try catch sats runt som kan ta hand om exceptions vid konverteringen och returnera null, samma som t= “object” o as MyOwnType. Exceptions tar lang tid och att skapa IEnumerator objekt tar langt tid.
    +1

  7. Morten Kristiansen added on 7. oktober 2010

    Man kan ikke sige der er mange bud på netop denne brainer; men Erik Haggstrom får ”kradset” lidt i lakken. Sagen er den at foreach bruger en IEnumerator til at iterere over listen. For hvert loop foretages to kald til virtuelle metoder: IEnumerator.get_Current og IEnumerator.MoveNext.

    Hvis man i stedet bruger en integer som index, vil man i loopen kunne nøjes med et enkelt virtuelt kald, nemlig ArrayList.get_Item(int index). Eftersom virtuelle kald er forholdsvis dyre, vil man derfor kunne observere en betydelig performance forskel.

    Nedenstående kode viser et eksempel på hvordan man kan måle performance forskellen. Målinger foretaget i en simulator viser at første metode tager ca. 25 ms, hvorimod den optimerede udgave kan nøjes med 6,6 ms – en besparelse på godt 70%. Gennemsnittet er beregnet på et rundt antal målinger, i dette tilfælde otte.

    Læg i øvrigt mærke til at Count gemmes i en lokal variabel umiddelbar før den sidste sløjfe. Eftersom compileren ikke kan gennemskue at Count er konstant mens sløjfen eksekveres, følgelig vil den kalde ArrayList.get_Count() for hvert loop – med degradering af performance til følge.

    [DllImport("coredll.dll")]
    public static extern UInt32 GetTickCount();

    public void TestPerformance()
    {
    System.Collections.ArrayList a =
    new System.Collections.ArrayList(10000);

    // Fill arraylist with something simple
    for (int i = 0; i < a.Capacity; ++i)
    {
    a.Add(i);
    }

    // Use IEnumerator
    int sum1 = 0;

    UInt32 TStart = GetTickCount();
    foreach (int n in a)
    sum1 += n;
    UInt32 t1 = GetTickCount() – TStart;

    // Use integer indexer
    int sum2 = 0;
    TStart = GetTickCount();
    int Count = a.Count;
    for(int i=0; i<Count; ++i)
    sum2 += (int) a[i];
    UInt32 t2 = GetTickCount() – TStart;

    MessageBox.Show(string.Format("t1={0} ms, t2={1} ms, sums={2} and {3}", t1, t2, sum1, sum2));
    }

Hvad mener du?