databájt 2010.11.08. 13:51

How to beat the compiler

 Jó napot, jó kódolást! 

Azon tűnődtem, mi történik, ha jószándék ide, cache-elés oda, extrém terhelés van egy metóduson?

Mondjuk több tízezres nagyságrendű hívásszám? Befolyásolja-e mérhetően a lookup eredményének felhasználási módja a futás idejét?

A válasz az alább látható fejtegetés végén található. 

Lássuk a kiindulási állapotot. 

Adott egy metódus: 


public string GetCached(int key)

{
        // Hit Count | Avg Time (ticks) | Time (ticks)
        string value = null; // 91 615 | 508 | 46 547 159

       if (!SomeProperty.TryGetValue(key, out value)) // 91 615 | 13 057 | 1 196 238 202
               value = SomeProperty.DefaultValue; // 652 | 6 931 | 4 519 494

       return value; // 91 615 | 508 | 46 547 159
}


 

Láthatjuk, hogy az első sorban adott null értéket rögtön felülírjuk a TryGetValue() metódus eredményével, ezért az az utasítás elhagyható volna.

Érdekes, hogy a C# fordító nem veszi ezt észre, ez kapásból 46 millió (!!!) tickbe kerül nekünk. 

A TryGetValue() sor érdekessége továbbá, hogy a negálás művelet minden kiértékelés után le fog futni, márpedig ez csak ritkán (lásd később) szükséges.

Írjuk át úgy a kódunkat, hogy az első sorban az értékadást elhagyjuk, és a negálást elhagyjuk. 

Így alakul a kód: 


public string GetCached(int key)

{
        // Hit Count | Avg Time (ticks) | Time (ticks)

       string value; // 91 615 | 0 | 0
        if (SomeProperty.TryGetValue(key, out value)) // 91 615 | 11 597 | 1 062 487 321

             return value; // 90 963 | 441 | 40 138 767
        return SomeProperty.DefaultValue; // 652 | 7 049 | 4 596 520
}


 

Látható, hogy az értékadás elhagyását illetve a default érték kezelését megváltoztatva újabb súlyos tick milliókat nyertünk.

Az, hogy kb. 91000-szer negáltunk egy boolean-t , amit később nem használtunk, itt durván 133 750 000 ticket jelent. Az sok. Nagyon sok. 

Az utolsó két sort összevetve az előző változat kódjával minimális eltérést (kb. 600 000 tick) láthatunk, ez érdekes, de akár mérési hiba is lehet.

Százalékban kifejezve az első és második megoldás lényegi különbségét:

 

Before

 

After

Line

Avg time

Total time

 

Line

Avg time

Total time

1

508

46 547 159

 

1

0

0

2

13 057

1 196 238 202

 

2

11 597

1 062 487 321

3

6 931

4 519 494

 

3

441

40 138 767

4

508

46 547 159

 

4

7 049

4 596 520

Total

 

1 293 852 014

 

Total

 

1 107 222 608

             
 

Before:

1 293 852 014

       
 

After:

1 107 222 608

       
 

Diff:

16,856%

       

 

 

Hát igen. Durván 17%-al gyorsabb lett a kódunk.

 

Gyorsabb. Lehetne ennél is gyorsabb? – Adódik a kérdés. 

Debizony hogy... Ugyanis még 2 metódus hívását megspórolhatjuk. Hogy nincs is hívva csak a TryGetValue()? Dehogynincs.

Ne  feledjük, hogy minden property gettelés a háttérben metódushívást jelent, esetünkben get_SomeProperty() nevűt. 

Lássuk, mi történik, ha galád módon nem használjuk az oly sokat propagált property mechanizmust! 


public string GetCached(int key)
{
       // Hit Count | Avg Time (ticks) | Time (ticks)
       string value; // 91 615 | 0 | 0
       if (_somePropertyField.TryGetValue(key, out value))  / / 91 615 | 10 621 | 973 096 244

       return value; // 90 963 | 290 | 26 381 642
       return _somePropertyField.DefaultValue; // 652 | 5 888 | 3 839 036
}


 Hoppá. Nem is olyan elhanyagolható az a két metódushívás. Hogy mennyire nem? Hasonlítsuk össze a legutolsó eredményt az elsővel: 

Before

 

After

Line

Avg time

Total time

 

Line

Avg time

Total time

1

508

46 547 159

 

1

0

0

2

13 057

1 196 238 202

 

2

10 621

973 096 244

3

6 931

4 519 494

 

3

290

26 381 642

4

508

46 547 159

 

4

5 888

3 839 036

Total

 

1 293 852 014

 

Total

 

1 003 316 922

             
 

Before:

1 293 852 014

       
 

After:

1 003 316 922

       
 

Diff:

28,957%

       

 

Bizony, bizony. Durván 29% az eltérés. Az eltérés majdnem felét az adja, hogy 2 property get helyett 2 field getet használunk.

Kriminális. Hogy ezt miért nem csinálja így automatikusan C# compiler? Na, az jó kérdés.

 

Tanulság 

Röviden összefoglalva:

·         A C# compiler nem szűri ki a legnyilvánvalóbb pazarlást sem (value = null a TryGetValue() előtt)

·         A property get-nek mérhető hátránya van a sima field get-tel szemben (~20%)

·         Kellően nagy számban a negálás művelete sem elhanyagolható (~90.000 hívásnál ~46 millió tick)

Szólj hozzá!

A bejegyzés trackback címe:

https://dotnot.blog.hu/api/trackback/id/tr344295567

Kommentek:

A hozzászólások a vonatkozó jogszabályok  értelmében felhasználói tartalomnak minősülnek, értük a szolgáltatás technikai  üzemeltetője semmilyen felelősséget nem vállal, azokat nem ellenőrzi. Kifogás esetén forduljon a blog szerkesztőjéhez. Részletek a  Felhasználási feltételekben és az adatvédelmi tájékoztatóban.

Nincsenek hozzászólások.
süti beállítások módosítása