Archief - [PROG][.NET] Illustratie waarom StringBuilder een goed idee is

Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.

Albireo

Legacy Member
Ik was in m'n design patterns boek code tegengekomen om te timen hoelang een methode erover doet om afgehandeld te worden en die wou ik eens uitproberen. En omdat ze altijd zeggen om StringBuilder te gebruiken i.p.v. string als je veel bewerkingen op die string moet uitvoeren heb ik eens getest hoelang het duurt om 1 000 000 char's aan mekaar te plakken; 1 keer met StringBuilder.Append en 1 keer met String+=

resultaat
- via String: 982591ms (16 minuten)
- via StringBuilder: 54ms

Code:
RandomWordCreator creator = new RandomWordCreator();
            creator.Length = 1000000;
            Console.WriteLine(ShowTimerCommand.TimeThis(new Command(creator.Create)).TotalMilliseconds);
            Console.WriteLine(ShowTimerCommand.TimeThis(new Command(creator.CreateString)).TotalMilliseconds);

Code:
public class RandomWordCreator {
        private static readonly char[] charset=new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
        private string randomword;
        private int length;
        public int Length {
            set {
                this.length = value;
            }
        }
        public string RandomWord {
            get {
                return this.randomword;
            }
        }
        public void Create() {
            this.randomword=CreateRandomWord(this.length);
        }
        public void CreateString() {
            this.randomword = CreateRandomString(this.length);
        }
        public static string CreateRandomWord(int length) {
            StringBuilder sb = new StringBuilder(length);
            Random rnd=new Random();
            for (int i = 0; i < length; i++) {
                sb.Append(RandomWordCreator.charset[rnd.Next(26)]);
            }
            return sb.ToString();
        }
        public static string CreateRandomString(int length) {
            string sb="";
            Random rnd = new Random();
            for (int i = 0; i < length; i++) {
                sb+=(RandomWordCreator.charset[rnd.Next(26)]);
            }
            return sb;
        }
    }
    public delegate void Command();
    public class ShowTimerCommand {
        public static void Snooze() {
            System.Threading.Thread.Sleep(2000);
        }
        public static TimeSpan TimeThis(Command c) {
            DateTime t1 = DateTime.Now;
            c.Invoke();
            return DateTime.Now.Subtract(t1);
        }
    }

(voor het geval iemand nog moest twijfelen aan het nut van StringBuilder)

killgore

Legacy Member
Ehm, iedereen die 2 seconden de achtergrond opzoekt weet dit hoor :p.

Simpele reden is dat StringBuilder een buffer bijhoudt en deze desnoods verlengt. String zelf is statisch, elke keer je de string wijzigt (append, remove, ...) wordt uw huidige string verwijderd en een nieuwe aangemaakt :) (en dan heb je nog temp vars en zo).

Da Turtle

Legacy Member
Ik denk dat het ook wel duidelijk ging zijn met 10 000 chars, dan moet je geen 16 minuten wachten :p.

//edit: Ik dacht even aan Java

Albireo

Legacy Member
Da Turtle zei:
Ik denk dat het ook wel duidelijk ging zijn met 10 000 chars, dan moet je geen 16 minuten wachten :p.

982591ms / 54ms (1 000 000) is veel indrukwekkender dan 42ms / 1ms (10 000) :D

killgore

Legacy Member
ook niet echt een logische benchmark he.

Bij de stringbuilder gaan ze 3 keer appenden en gebruiken ze empty constructor

bij de string gaan ze 2x gaan appenden en gebruiken ze de string init constructor. Dat is sowieso al een operatie minder die je bij stringbuilder ook kan gaan doen :p.

Dat zijn benchmarks die afhangen van slechte/goede code, zo kan ik ook gaan bewijzen dat php 10x sneller is als pure assembler.

edit: een 2e reden is dat je hier opeenvolgende code hebt die de compiler in geval 2 (de string) naar 1 statement zal herleiden:

string s = i.ToString() + i.ToString() + i.ToString();

iets dat ook veel makkellijker te optimaliseren is & maar 1x geheugen (re)allocatie vraagt, iets dat bij de stringbuilder ook niet snel zal gebeuren. Een vrij onrealistische situatie imho.

edit: het is wel wat sneller heb ik gemerkt, maar met iets logischere codes die ik gebruikte werd het verschil echt minimaal :p:

Code:
using System.Text;
using System;

namespace test
{
    class Program
    {
        static int loops = 1000000;
        [Benchmark]
        public static void TestBuilder1()
        {
            for (int i = 0; i <= loops; i++)
            {
                StringBuilder s = new StringBuilder();
                s.Append(i.ToString());
                s.Append(i.ToString());
                s.Append(i.ToString());
            }
        }
        [Benchmark]
        public static void TestString1()
        {
            for (int i = 0; i <= loops; i++)
            {
                string s = i.ToString();
                s = s + i.ToString();
                s = s + i.ToString();
            }
        }
        [Benchmark]
        public static void TestBuilder2()
        {
            for (int i = 0; i <= loops; i++)
            {
                StringBuilder s = new StringBuilder(i.ToString());
                s.Append(i.ToString());
                s.Append(i.ToString());
            }
        }
        [Benchmark]
        public static void TestBuilder3()
        {
            for (int i = 0; i <= loops; i++)
            {
                StringBuilder s = new StringBuilder(i.ToString());
                s.Append(s);
                s.Append(s);
            }
        }
        [Benchmark]
        public static void TestString3()
        {
            for (int i = 0; i <= loops; i++)
            {
                string s = i.ToString();
                s += s;
                s += s;
            }
        }
        [Benchmark]
        public static void TestBuilder4()
        {
            for (int i = 0; i <= loops; i++)
            {
                String tmp = i.ToString();
                StringBuilder s = new StringBuilder(tmp);
                s.Append(tmp).Append(tmp);
            }
        }
    }
}

Hiervoor heb ik dit benchmark programma gebruikt: http://www.yoda.arachsys.com/csharp/benchmark.html

het verschil tussen TestString3 en TestBuilder4 is iets van 0,01 seconde op zovele loops :). En ik blijf het een onrealistische situatie vinden, je zal hier veeleer formatters voor gaan gebruiken dan appends. Ik denk dat in elke "normale" append situatie builder sneller zal zijn.

UniKorn

Legacy Member
Een ander verschil tussen beiden is dat bij

+= ipv een stringbuilder er 2x geheugen ingepakt wordt.

Emerxill

Legacy Member
Conclusie: gebruik StringBuffer als ge bewerkingen wil doen op de tekst.
Als er geen bewerkingen nodig zijn, gebruik dan String.
Het archief is een bevroren moment uit een vorige versie van dit forum, met andere regels en andere bazen. Deze posts weerspiegelen op geen enkele manier onze huidige ideeën, waarden of wereldbeelden en zijn op sommige plaatsen gecensureerd wegens ontoelaatbaar. Veel zijn in een andere tijdsgeest gemaakt, al dan niet ironisch - zoals in het ironische subforum Off-Topic - en zouden op dit moment niet meer gepost (mogen) worden. Toch bieden we dit archief nog graag aan als informatiedatabank en naslagwerk. Lees er hier meer over of start een gesprek met anderen.
Terug
Bovenaan