Proč je potřeba si to neustále uvědomovat a co z toho vyplývá pro vás a váš styl programování?
Immutable
K immutable toho bylo napsáno již dost, takže jen ve stručnosti:Pokud je proměnná immutable, znamená to, že je-li jednou vytvořena, nelze ji následně změnit.
Pokud změnu požadujeme, je nová hodnota zapsána na nové místo v paměti. Pokud tedy sčítáme dva řetězce, je napřed vytvořeno volné místo o velikosti součtu jejich délek a do tohoto místa jsou oba řetězce vkopírovány.
Příklad z praxe
Potřeboval jsem analyzovat nějaký řetězec tak, že jsem jej postupně procházel a pro každou pozici vyzobl řetězce délky 1, 2, 4 a 9 znaků. Ty jsem následně s čímsi porovnával... Z určitých důvodů jsem potřeboval zarovnat tyto řetězce na požadovanou délku pomlčkami, což jsem vtipně realizoval jejich přičtením k hlavnímu textu a teprve následně jsem to vyzobal Substringem. Elegantní...
Realizace vypadala nějak takto:
private static void Test1(string text)
{
for (var iCitac = 0; iCitac < text.Length; iCitac++)
{
var znak1 = (text + "-").Substring(iCitac, 1);
var znak2 = (text + "--").Substring(iCitac, 2);
var znak4 = (text + "----").Substring(iCitac, 4);
var znak9 = (text + "---------").Substring(iCitac, 9);
}
}
Fungovalo to skvěle, sice u větších souborů zpracování chvíli trvalo, ale svedli jsme to na databázi. Ani stín podezření...
Po pár letech jsem řešil obdobnou věc a výše uvedenou rutinku jsem na prasáka zkopíroval. V testech to jelo jedna báseň, ale jak jsem tomu předhodil 200 kB dat, nemohl jsem se dočkat výsledků.
Vzpomněl jsem si na Robertovu přednášku (viz. Další informace) a začal pátrat. Po drobné úpravě už bylo vše, jak má být:
private static void Test2(string text)
{
var temp = text + "---------";
for (var iCitac = 0; iCitac < text.Length; iCitac++)
{
var znak1 = temp.Substring(iCitac, 1);
var znak2 = temp.Substring(iCitac, 2);
var znak4 = temp.Substring(iCitac, 4);
var znak9 = temp.Substring(iCitac, 9);
}
}
Schválně, tipněte si, kolikrát se zpracování řetězce zrychlilo při jeho 200 kB délce...
Prozradím v dalším odstavci.
Proč?
Jak už víme ze začátku článku, při sčítání řetězců je v paměti vyhrazeno nové místo pro výsledek, tam je stávající řetězec přesunut a k němu je dokopírována zbývající část. V kontextu 200 kB souboru procházeného znak po znaku to znamená celkem 200000x kopírovat v paměti 200000 znaků. A ještě 4x pro znaky 1,2,4 a 9, takže celkem 800000x.
Tím, že jsem řetězec prodloužil jen jednou na začátku (nové) metody, jsem se veškeré té dřině vyhnul.
Výsledek je tady:
1300x rychlejší! Necelá desetina sekundy oproti téměř dvěma minutám, to už stojí za tu námahu, ne? |
Žádné komentáře:
Okomentovat