Tuning, aneb optimalizace kódu

Ondřej Linhart       4. 4. 2008       VB.NET, Optimalizace, WinForms       8047 zobrazení

Tipy a triky pro optimalizaci kódu tak, aby běžel co nejefektivněji.

Úvod

Optimalizace kódu je důležitou součástí vývoje aplikace. Správný kód by měl být napsán co nejefektivněji, což se potom ve výsledku projeví ve výkonnosti celé aplikace. Tento článek by vám měl pomoct při optimalizaci těch nejzákladnějších částí kódu, které se právě díky nevhodnému použití stávají brzdou celé aplikace.

Hodnotové typy a referenční typy

Data hodnotových typů (základní datové typy a struktury) jsou izolovaně udržována v paměti (v zásobníku - Stack), to znamená že více proměnných se nemůže odkazovat na stejnou hodnotu. Referenční typy jsou přesný opak - proměnné udržují pouze ukazatel na data v paměti, je tedy možné sdílet jedinou instanci více proměnnými (ukazatele jsou na tzv. hromadě - Heap). Práce se zásobníkem je rychlejší než práce s hromadou, zde je tedy nutné zvážit, zda-li se vyplatí použít hodnotové typy místo referenčních. Obecně je lepší použít hodnotové typy v případě, že nepotřebujete flexibilitu referenčního typu (třídy). Efektivita hodnotových typů však klesá s jejich velikostí. Podle MSDN může volba typu způsobit neznatelný až třicetiprocentní nárůst výkonu.

Early Binding a Late Binding

Jestliže nadeklarujete proměnnou jako specifickou třídu, potom je tzv. Early Bound, což znamená konkrétního datového typu. To umožňuje kompilátoru předem provést optimalizace (ověření datového typu a členů datového typu), které samozřejmě vedou ke zvýšení výkonu. V opačném případě lze proměnnou nadeklarovat jako typ Object, což je sice mimořádně flexibilní, ovšem za cenu snížení výkonu (nelze provést optimalizace). Takováto proměnná se nazývá Late Bound. Proto silně doporučuji vždy mít zapnuto Option Explicit On, což vyžaduje striktní deklaraci datového typu (v opačném případě to bude vždy Object, tak jako ve VB6 to byl vždy Variant).

Velikost datového typu

Nejefektivnější datový typ je takový, jaký je nativní pro danou platformu. Dnes je zatím nejběžnější platforma 32-bitová, proto efektivita celočíselných typů je následující (od nejlepšího k nejhoršímu): Integer, UInteger, Long, ULong, Short, Byte, SByte. U zlomkových čísel je to Double, Single a Decimal. Výkon lze výrazně zvýšit také vypnutím možnosti Remove integer overflow checks na kartě Compile/Advanced Compile Optimizations... ve vlastnostech projektu. Jestliže je tato možnost zapnuta (výchozí), kompilátor pokaždé ověřuje zda-li proměnná typu Integer nepřetekla a v případě přetečení nastane vyjímka. V opačném případě proměnná přeteče bez vyvolání vyjímky.

Boxing a Unboxing

Chcete-li uložit hodnotový typ do referenčního typu (například Integer do proměnné typu Object), musí .NET Runtime provést zapouzdření, tzv. Boxing. To se skládá ze zkopírování hodnoty, uložení hodnoty do nově alokovaného objektu a uložení informací o typu. Z toho plyne, že toto je náročná operace, které je třeba se vyvarovat. Unboxing je opačný postup, kdy z proměnné typu Object získáme konverzí původní Integer. Doporučuji vždy mít zapnuto Option Strict On, což vás bude nutit používat explicitní konverzi (CType, CObj), která sice nezabrání Boxingu, ovšem zamezí nechtěnému přiřazení hodnotového typu do referenčního.

Pole

Vyvarujte se používání vícerozměrných polí. Jednorozměrná pole jsou narozdíl od vícerozměrných optimalizována. Pokud nezbytně potřebujete použít více rozměrů, použijte raději "pole polí", tj. místo values(0, 0) použijte values(0)(0). Rozdíl ve výkonnosti jednorozměrných a vícerozměrných polí může být až 30%. Mimochodem vícerozměrná pole nevyhovují specifikaci CLS (Common Language Specification), takže když už je používáte, měli byste příslušné metody označit atributem CLSCompliant(False).

Kolekce

Když procházíte kolekci, můžete použít buď For nebo For Each. Jestliže budete přidávat, odstraňovat nebo přesouvat jednotlivé položky, je lepší použít For. For vám také umožňuje procházet kolekci pozpátku, což v případě For Each nelze. Kromě toho všechny kolekce odvozené od základní třídy System.Collections.CollectionBase vyvolají vyjímku jestliže v průběhu procházení pomocí For Each změníte obsah kolekce (přidáte nebo odeberete položku). Jestliže chcete do kolekce přidat více položek, snažte se použít metodu AddRange, protože poskytuje vyšší výkon než přidávání jednotlivých položek v cyklu.

Kolekce versus Pole

  • Obecně je práce s polem rychlejší než práce s kolekcí, proto když vám stačí metody obsažené ve třídě Array (představuje pole), dejte jí přednost před kolekcí.
  • Potřebujete-li přístup pomocí indexu, použijte pole.
  • Potřebujete-li přístup pomocí klíče, použijte kolekci (ideálně Dictionary(Of TKey, TValue)).
  • Potřebujete-li často vkládat a odstraňovat položky, použijte kolekci.

Vlastnosti, proměnné a konstanty

S proměnnými se pracuje rychleji než s vlastnostmi. Proměnná je jednoduše místo alokované v paměti, kdežto vlastnost jsou ve skutečnosti dvě metody - jedna pro čtení a druhá pro zápis (nebo jen jedna pro čtení, je-li vlastnost pouze pro čtení). Volání metod je samozřejmě pomalejší než přímý přístup na proměnnou. Z hlediska architektury by však třídy neměly obsahovat veřejné proměnné, vždy raději vlastnosti (z hlediska budoucího rozšíření, například validace hodnoty). Nejrychlejší přístup je ke konstantám, což jsou hodnoty napevno zkompilované v kódu (vyjímkou jsou konstanty typu DateTime a Decimal).

Operátory

  • Jestliže dělíte celá čísla a nepotřebujete zbytek, použijte operátor pro celočíselné dělení (\), který může být údajně až 10x rychlejší než operátor pro dělení se zbytkem (/).
  • Jestliže potřebujete "sečíst" více hodnot typu String, použijte specializovaný operátor pro sjednocování Stringů "&" místo obecného "+", který je v případě sčítání Stringů pomalejší.

Okamžitá inicializace vs. Líná inicializace

Máte-li třídu, která na základě parametrů předaných v konstruktoru vrací nějaké vypočítané hodnoty pomocí svých vlastností a/nebo metod, máte v zásadě dva způsoby jak hodnoty vypočítat. První způsob (Okamžitá inicializace) spočívá ve vypočtení hodnot předem již v konstruktoru třídy. Tento způsob je vhodný v případě, že k vlastnostem/metodám této třídy bude velmi častý přístup. Druhý způsob (Líná inicializace) spočívá ve vypočtení hodnot "na poslední chvíli", to znamená teprve v momentě kdy je to potřeba (přístup k dané vlastnosti/metodě). Tento způsob se vyplatí tam, kde k vlastnostem/metodám třídy není častý přístup.

Sjednocení cyklů

Snažte se minimalizovat počet cyklů sjednocením do jednoho pokud je to možné.

Dim customerNames(99) As String
Dim customerSurnames(99) As String

'Špatně:
For index As Integer = 0 To 99
  customerNames(index) = "Jan"
Next
For index As Integer = 0 To 99
  customerSurnames(index) = "Novák"
Next

'Správně:
For index As Integer = 0 To 99
  customerNames(index) = "Jan"
  customerSurnames(index) = "Novák"
Next

Nikdy nepoužívejte náročné datové typy (Single, Double, Decimal) jako index při procházení cyklem!!!

Rozhodování

Při rozhodování pomocí Select Case se snažte nejpravděpodobnější případy umístit na začátek bloku Select Case.

Short-Circuiting (zkratování)

Zkratování je způsob vyhodnocování více výrazů, při kterém lze kontrolu zbývající části podmínky vynechat a tím urychlit průběh programu.

výraz1 výraz2 And    AndAlso Or     OrElse
True   True   True   True    True   True*
True   False  False  False   True   True*
False  True   False  False*  True   True
False  False  False  False*  False  False

* výraz2 se nevyhodnocuje

If (sex = "Female") AndAlso (age >= 18) Then
  'Pokud pohlaví nebude ženské, věk se ani nebude vyhodnocovat
End If

If (sex = "Male") And (age >= 18) Then
  'Věk se bude zbytečně vyhodnocovat i přesto že pohlaví nebude ženské
End If

Přístup k členům třídy

Pokud to jde, používejte klíčové slovo With, čímž se vyvarujete opětovného zbytečného volání.

'Špatně:
Form1.Controls.Item("Button1").Text = "Tlačítko 1"
Form1.Controls.Item("Button1").Enabled = False

'Správně:
With Form1.Controls.Item("Button1")
  .Text = "Tlačítko 1"
  .Enabled = False
End With

Některé vlastnosti nebo metody mohou vracet nové instance objektů. Jestliže neznáte interní implementaci takové vlastnosti, pište kód vždy tak, jako by vlastnost pokaždé vracela novou instanci objektu. To znamená pokud budete například v cyklu pracovat s touto vlastností, uložte si ji předtím do dočasné proměnné. Pokud by vlastnost skutečně vracela novou instanci při každém přístupu, procházeli byste vlastně při každém průchodu cyklem úplně jiná data a kromě toho by byl kód pomalejší vzhledem k režii na vytvoření nové instance při každém průchodu. V případě vlastnosti musíte ještě připočítat režii na volání metod Get/Set.

'Špatně:
For index As Integer = 0 To 9
  Dim name As String = Form1.Controls(index).Name
Next

'Správně:
Dim controls As ControlCollection = Form1.Controls
For index As Integer = 0 To 9
  Dim name As String = controls(index).Name
Next

Konverze typů

Pokud mají oba typy které chcete konvertovat mezi sebou vzájemný dědičný vztah, použijte konverzní funkci DirectCast místo CType. DirectCast poskytuje vyšší výkon než CType.

Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs)
  Dim b1 As Button = DirectCast(sender, Button)
  'Třída Button je zděděná od Object, tudíž konverze bude fungovat
End Sub

Vyjímky

Jestliže používáte v kódu blok Try ... Catch ... Finally, měli byste jej používat pouze na ošetřování kritických chyb, protože má nezanedbatelně velkou režii. Nesprávný způsob použítí Try ... Catch ... Finally je řízení toku kódu, zjednodušeně řečeno vyhození vyjímky jen z toho důvodu, aby se skočilo jinam.

'Špatně:
Try
  Dim x As Integer = 10
  If x <= 10 Then
    Throw New Exception()
  End If
  Debug.WriteLine(x)
Catch
Finally
  Debug.WriteLine("Konec")
End Try

'Správně:
Try
  Dim x As Integer = 10
  If x <= 10 Then Return
  Debug.WriteLine(x)
Catch
Finally
  Debug.WriteLine("Konec")
End Try

Práce s textovými řetězci

Pokaždé když změníte proměnnou typu String, je v paměti ponechána stávající instance a vytvořena nová. Frekventovanou změnou proměnné typu String tedy zbytečně plníte paměť. Proto existuje třída StringBuilder, která vždy drží jedinou instanci Stringu i po změnách.

'Špatně:
Dim numbers As String
For index As Integer = 0 To 9
  numbers &= index.ToString()
Next
'V paměti existuje 10 instancí Stringu
Debug.WriteLine(numbers)

'Správně:
Dim numbers As New StringBuilder(10)
For index As Integer = 0 To 9
  numbers.Append(index.ToString())
Next
'V paměti existuje 1 instance Stringu
Debug.WriteLine(numbers.ToString())

'Špatně:
Dim abc As String = String.Empty
abc &= "a"
abc &= "b"
abc &= "c"

'Správně:
Dim abc As String = "a" & "b" & "c"

Při převádění hodnot na typ String používejte metodu ToString, protože je rychlejší než funkce CStr.

Metody

Při psaní metod je třeba zvážit, zda-li se metoda bude používat ještě na dalších místech. S mechanismem volání metody je spojena určitá režie, která se projeví hlavně v cyklech. Máte-li tedy jistotu že kód procedury se nebude používat ještě na jiném místě, bude kód rychlejší napíšete-li ho přímo do těla cyklu.

Kompilace

Jak jistě víte, aplikace se kompiluje do tzv. MSIL kódu, což je platformově nezávislý binární kód (stejně jako Bytecode v Javě), který je kompilován podle potřeby do nativního kódu procesoru během činnosti programu (konkrétně při prvním volání metody). Kompilace podle potřeby (Just-In-Time) samozřejmě má nějakou režii a proto lze celou aplikaci předem předkompilovat do nativního kódu pomocí nástroje Ngen (Native Image Generator), což může výrazně urychlit spuštění aplikace. Na druhou stranu to ovšem může zpomalit některé často volané metody, protože nástroj Ngen neumí provádět takové optimalizace jako JIT kompilátor. Pro více informací:

http://msdn2.microsoft.com/en-us/library/6t9t5wcf(vs.71).aspx

Optimalizace zobrazení uživatelského rozhraní

  • Vyvarujte se zbytečného překreslování ovládacích prvků. Zavolejte metodu BeginUpdate před přidáním položek a EndUpdate po přidání položek u komponent typu seznam (ListView, ListBox...)
  • Při uživatelském vykreslování komponent se snažte překreslit jen potřebnou oblast, ne celou komponentu což bude mít za výsledek rychlejší odezvu.
  • Pokuste se kritická data načíst předem. Bude to sice pomalejší ze začátku, ale další práce s nimi bude potom rychlá.
  • Veškeré dlouhotrvající operace přepište na zpracování ve vláknech s možností přerušit danou operaci a ukazatelem stavu.

Minimalizace využití paměti

  • Mějte současně načteny jen ty instance formulářů, které zrovna potřebujete.
  • Používejte co nejméně ovládacích prvků a tyto ovládací prvky co nejvíce odpovídající dané funkčnosti (například je zbytečné používat TextBox tam, kde je text pouze pro čtení a stačí Label)
  • Vyvarujte se použití větších datových typů než potřebujete a to obzvlášť v polích a kolekcích.
  • Když dokončíte práci s velkým datovým typem (textové řetězce, pole, kolekce a další potenciálně velké datové typy) nastavte jej na Nothing.
  • Používejte blok Using ... End Using u tříd podporujících rozhraní IDisposable, většinou zapouzdřujících Unmanaged objekty.

 

hodnocení článku

1 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Mohlo by vás také zajímat

Řešené příklady v ASP.NET - díl 3.: Aplikace pro zamlouvání sedadel (část 3)

V této části si ukážeme, jak vygenerovat a naplnit tabulku, a jak napsat komponentu, která má vlastní serverové události. Nakonec si ukážeme, jak aktualizovat jen část stránky pomocí komponenty UpdatePanel.

Programování Windows Services

Services (služby) jsou jedním ze základních stavebních kamenů programování pro systémy Windows. V článku se budu věnovat jak obecným principům a doporučením, tak konkrétním implementačním postupům.

Práce s časovými pásmy a letním časem v aplikaci a databázi - díl 3.: DateTimeOffset v .NET Frameworku

DateTimeOffset je méně využívanou alternativou struktury DateTime v .NET Frameworku. Navíc dovoluje ukládat časové pásmo a pohodlně s ním pracovat.

 

 

Nový příspěvek

 

Diskuse: Tuning, aneb optimalizace kódu

Zdrávím,

jen se chci zeptat, zda jsem kapitolu se StringBuilderem pochopil správně.

Pokud udělám toto vytvoří se v paměti 3 instance stringu:

Dim abc As String = String.Empty
abc &= "a"
abc &= "b"
abc &= "c"

Pokud udělám toto vytvoří se jen jedna instance stringu:

Dim abc As String = String.Empty
abc = "a" & "b" & "c"

A pokud udělám toto tak jedna instance StringBuilderu:

Dim abc as New StringBuilder
abc.Append("a" & "b" & "c")

Pokud je to tak, je z posledních dvou případů vhodnější používat string nebo StringBuilder, pokud danou operaci dělám v cyklu cca 10 000 krát. (string by se samozřejmě nedeklaroval v každém cyklu znovu, jen by se měnil jeho obsah a u stringbuilderu by se v cyklu jen volala vlastnost ToString a měnila by se vlastnost length na nulu pro vyprázdnění)

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Pokud sestavujete String v cyklu, vždy použijte StringBuilder. Pokud použijete v cyklu operátor &, vytvoří se nová instance Stringu (to neplatí pokud použijete & jednou).

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Ano, tomu jsem z Vašeho výkladu porozuměl, ale zřejmě jsem svou otázku formuloval špatně. Zeptám se takto. Řetězec nevytvářím v cyklu, vytvořím jej jen jednou a následně ho pomocí StreamWriter.WriteLine vepíšu do souboru. Jsou v tomto případě následující 2 způsoby rovnocené, nebo pro spojování řetězců vždy lépe použít StringBuilder?

'První způsob
Dim abc As String = String.Empty
abc = "aaa" & "bbb" & "ccc"
strWriter.Writeline(abc)

'Druhý způsob
Dim abc as New StringBuilder
abc.Append("aaa" & "bbb" & "ccc")
strWriter.Writeline(abc.ToString)

Předpokládám, že pokud použiji operátor & v metodě append, nebude se v paměti tvořit žádná nová instance.

Jinak Váš článek je super. Už jsem podle rad v něm překopal půlku svého prográmku.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

V tomto konkrétním případě bude rychlejší první způsob, protože kompilátor optimalizuje výraz abc = "aaa" & "bbb" & "ccc" na abc = "aaabbbccc", kdežto ve druhém případě se bude volat metoda Append což má samozřejmě nějakou režii.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Díky za odpověď, přesně to jsem chtěl vědět.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Tuning, aneb optimalizace kódu

Moc pěkný článek, jen mě tu chybí jedna věc.

Načítám ze souboru pole a nevím, jakou bude mít na konci velikost (načítaná data se ještě před přidáním pole vyhodnocují). Proto nejprve vytvořím pole s 1000 prvky a když index přesáhne velikost pole, prostě mu změním velikost o dalších 1000 prvků pomocí Array.Resize(ref pole, pole.Length + 1000). Tohle asi nebude nejrychlejší řešení, proto bych se chtěl zeptat, jak nejrychleji změnit velikost pole. Nebo bude lepší využít kolekci (ačkoliv, potřebuji s polem pracovat celkem rychle, takže by bylo lepší řešení pomocí pole)?

Za odpověď díky.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Pokud předem nevíte počet prvků, je samozřejmě nejlepší použít kolekci (nejlépe List(Of T)). Kolekce vnitřně pracuje s polem a má optimalizované algoritmy pro zvětšení velikosti v případě, že kapacita nebude stačit. Kromě toho lze konstruktoru List(Of T) předat počáteční kapacitu.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Diskuse: Tuning, aneb optimalizace kódu

Nerozepsal byste se vice o "pole polí"

Celkem by me to zajimalo.

Jinak vyborny clanek, tak jak jsem od Vas zvykly.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Vícerozměrné pole:

'Definice
Dim values(1, 1) As Integer
'Zápis do
values(0, 0) = 1
values(0, 1) = 2
values(1, 0) = 3
values(1, 1) = 4
'Čtení z
Dim v1 As Integer = values(0, 0)
Dim v2 As Integer = values(0, 1)
Dim v3 As Integer = values(1, 0)
Dim v4 As Integer = values(1, 1)

Jednorozměrné pole polí - náhrada vícerozměrného pole

(představte si to jednoduše tak, že každý prvek pole určitého typu tvoří pole prvků stejného typu o libovolné délce):

'Definice
Dim values(1)() As Integer
'Zápis do (s implicitní inicializací)
values(0) = New Integer(1) {1, 2}
values(1) = New Integer(1) {3, 4}
'nebo s explicitní inicializací
ReDim values(0)(1)
ReDim values(1)(1)
values(0)(0) = 1
values(0)(1) = 2
values(1)(0) = 3
values(1)(1) = 4
'Čtení z
Dim v1 As Integer = values(0)(0)
Dim v2 As Integer = values(0)(1)
Dim v3 As Integer = values(1)(0)
Dim v4 As Integer = values(1)(1)

Doufám že tímto jednoduchým příkladem jsem to alespoň trochu osvětlil.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Velmi pěkný článek, akorát mě zaskočilo, že pole polí je rychlejší než vícerozměrné pole. Nedalo mi to a vyzkoušel jsem to u sebe, a vyšlo mi, že je to srovnatelné, o trochu rychlejší je opravdu pole polí, ale je to asi jen o 1 až 2 procenta. Zkoušel jsem různou velikost polí a různé metody čtení a zápisu, nutno ovšem podotknout, že inicializace pole polí bude asi o trochu pomalejší. Těžko říci, jak to je, kdoví jaké optimalizace se tam provádí, ale je to každopádně zajímavé. Nicméně 30% rozdíl se mi opravdu nalézt nepodařilo.

V praxi můžeme používat normálně vícerozměrná pole, protože nemusíme každý řádek zvlášť inicializovat, pokud chceme nějakou supervýkonnou aplikaci, pak je stejně asi lepší napsat ji v céčku.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
using System;
using System.Collections.Generic;
using System.Text;

namespace ArrTest
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime t = DateTime.Now;
            int[,] p = new int[1000, 1000];
            for (int k = 0; k < 100; k++)
            {
                int l1 = p.GetLength(0);
                int l2 = p.GetLength(1);
                for (int i = 0; i < l1; i++)
                {
                    for (int j = 0; j < l2; j++)
                    {
                        p[i, j] = i * j;
                    }
                }
            }
            Console.WriteLine(DateTime.Now - t);
            t = DateTime.Now;
            int[][] p1 = new int[1000][];
            for (int i = 0; i < p1.Length; i++)
            {
                p1[i] = new int[1000];
            }

            for (int k = 0; k < 100; k++)
            {
                int l1 = p1.GetLength(0);
                for (int i = 0; i < l1; i++)
                {
                    int l2 = p1[i].GetLength(0);
                    for (int j = 0; j < l2; j++)
                    {
                        p1[i][j] = i * j;
                    }
                }
            }
            Console.WriteLine(DateTime.Now - t);
            Console.ReadKey();
        }
    }
}

Pri zapnutych optimalizacich ~20% rozdil.

Take me to velmi nemile prekvapilo. Zajimalo by me, cim to je. Zda za to muze to nasobeni pri zjistovani indexu ve vicerozmernym poli, na druhou stranu pri poli poli se musi skakat vic v pameti...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Jak jsem již uváděl, jednorozměrné pole je optimalizováno, vícerozměrné pole optimalizováno není. Použijete-li pole polí, těžíte právě z této optimalizace, protože pole v poli je rovněž optimalizováno.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Tuning, aneb optimalizace kódu

a měl bych 2 dotazy. jednak ke konstrukci "With". POužití této konstrukce má i nějaký výkonnostní efekt, jak píšete? Já měl vždy představu, že se jedná pouze o (velice šikovnou) pomůcku, jak se při psaní kódu neupsat k smrti, případně jak zpřehlednit kód, ale myslel jsem si, že při kompilaci si to kompilátor doplní do plných volání proměných, metod či vlastností.

Můj druhý dotaz, či prosba, by byl trošičku komplexnější. Nebyl byste té ochoty a jako pokračování či rozšíření článku nedoplnil i související oblast "ladění kódu"? Nějaké praktické rady či postupy, jak na to.

Něco, co by mně (a věřím i mnoha ostatním) pomohlo vyřešit problémy podobné těm, které třeba teď řeším já: Mám aplikaci, kterou v nějakém vývojovém mezistupni testuji. Fůru chyb a optimalizací jsem samozřejmě odchytil již v ranném stádiu testování (v rámci debugeru Visual Studia). Teď probíhá druhá fáze testování tak říkajíc přímo v boji, no a dochází k tomu, že se mi aplikace vždy po nějakém čase "odporoučí" do věčných lovišť. Problémem je ovšem nalézt příčinu. V určitých fázích chodu aplikace si dělám kontrolní (trace) výpisy - kde zrovna jsem, jakou činnost právě provádím, jaký soubor otevírám, jaké mám využití paměti a kolik procesů právě spotřebovávám (původně jsem totiž měl problémy se špatně ošetřenou pamětí), leč ani z těchto výpisů mi nelze zjistit žádné zákonitosti- paměť ani počet procesů nerostou, spadne to pokaždé u zcela jiné činnosti, čas po kterém to spadne se pohybuje od několika hodin po několik dnů (není výjimkou, že mi to frčí téměř týden, ale někdy to musím nahazovat i 2x za den).

Tedy byl bych vděčen za jakoukoliv radu, jak řešit takovéto ladění aplikace, je-li možno získat do nějakého log souboru systémové hlášky (jako třeba při běhu přímo ve VS zachytává okno debog), nebo jak se dopátrat příčiny těch zcela nepravidelných pádů.

nahlásit spamnahlásit spam 1 / 1 odpovědětodpovědět

Když se podíváte na ten konkrétní příklad který uvádím u With, tak prvním způsobem je to na dva řádky - to znamená že na pozadí se musí 2x vyzvednout položka z kolekce na pozici "Button1", zatímco v případě s použitím With se položka vyzvedne pouze 1x a hromadně se jí nastaví uvedené vlastnosti. Režie na dvojnásobné vyzvednutí položky je sice víceméně zanedbatelná, ovšem optimalizace to je. Takovéto věci se nejvíce projevují v cyklech o řádově milionech průchodů kde už může být rozdíl v sekundách.

Ohledně vašeho druhého dotazu to bude trochu problematické. Hledat chyby jaké popisujete je skutečně mor největšího kalibru, speciálně u rozsáhlých projektů a když se chyby projevují náhodně a nepravidelně. V takovém případě máte dvě možnosti: První z nich by bylo zavést do aplikace tzv. globální handler, který bude odchytávat vyjímky na úrovni celé aplikace, tj. kdekoliv nastane vyjímka, vždy se dostane do tohoto globálního handleru a v něm se ideálně zapíše do systémového protokolu nebo do logovacího textového souboru ve složce aplikace. Vzhledem k tomu, že z vyjímky můžete dostat celou historii volání, třídu a metodu ve které k vyjímce došlo a dokonce i řádek ve zdrojovém souboru, je tato metodika vysoce efektivní a mám s ní pozitivní zkušenosti.

Druhá možnost je zavést Unit Testing, což je sice profesionální ale časově velice náročná metodika, která pomáhá udržovat integritu celé aplikace (opět se používá především u rozsáhlých projektů). Velice zjednodušeně řečeno se jedná o to, že kdykoliv napíšete novou třídu nebo přidáte funkčnost do třídy existující, napíšete do zvláštního testovacího projektu zároveň protitřídu, která ověřuje funkčnost. Napíšu-li tedy třídu, která bude provádět sčítání a odčítání, napíšu také druhou testovací třídu která bude do metod předávat hodnoty a skutečné výsledky porovnávat s očekávanými výsledky. Unit Testing je součást Visual Studia 2008 Team System, nebo se dá provozovat s Open Source (fuj) testovacím nástrojem NUnit. Problematika testování je však tak komplexní, že by to vyžadovalo samostatný článek.

nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback