Dlaczego foreach jest gorsze od for (beta)

2

MSM i somekind wątpią w moje twierdzenia o gorszej wydajności foreach w stosunku do for. Żeby nie było że coś mi się przyśniło na szybko postaram się nakreślić problem.

Niech przykładem będzie taki oto prosty program.

 
    class Program
    {
        static Random rand = new Random((int)DateTime.Now.Ticks);

        static List<T> CreateTestList<T>(int size, Func<T> newItem)
        {
            List<T> list = new List<T>(size);
            for (int i = 0; i < size; i++)
                list.Add(newItem());
            return list;
        }

        static void Test<T>(int size, Func<T> newItem)
        {
            List<T> l1 = CreateTestList<T>(size, newItem);

            DateTime d1 = DateTime.UtcNow;
            for (int i = 0; i < l1.Count; i++)
            { }
            DateTime d2 = DateTime.UtcNow;
            foreach (var x in l1)
            { }
            DateTime d3 = DateTime.UtcNow;

            Console.WriteLine("for    : {0,-10}", (d2 - d1).TotalMilliseconds);
            Console.WriteLine("foreach: {0,-10}", (d3 - d2).TotalMilliseconds);
        }

        static void Main(string[] args)
        {
            int size = 1000000;

            Console.WriteLine("int - value type");
            Test<int>(size, () => rand.Next());
            Console.WriteLine("FooClass1 - reference type");
            Test<FooClass1>(size, () => new FooClass1 { I1 = rand.Next() });
            Console.WriteLine("DateTime - value type");
            Test<DateTime>(size, () => DateTime.Now);
            Console.WriteLine("string - refernece type, immutable type");
            Test<string>(size, () => rand.Next().ToString());
        }
    }

    public class FooClass1
    {
        public int I1 { get; set; }
    }

Co jest przyczyną zła jeśli chodzi o foreach? Enumerator!

Tak reflector pokazuje zdeassemblowany kawałek dla foreach.

using (List<int>.Enumerator enumerator = list.GetEnumerator())
{
    while (enumerator.MoveNext())
    {
        CS$1$0000 = enumerator.Current;
    }
}

i jeszcze Enumerator<T>.MoveNext()

public bool MoveNext()
{
    List<T> list = this.list;
    if ((this.version == list._version) && (this.index < list._size))
    {
        this.current = list._items[this.index];
        this.index++;
        return true;
    }
    return this.MoveNextRare();
}

Myślę że dla wszystkich którzy przeczytają ten kod wszystko będzie jasne.
Poza tym jeśli ja nie jestem autorytetem :) to proponuję przeczytać
http://msdn.microsoft.com/en-us/library/ms973839.aspx cześć Use For Loops for String Iteration—version 1
"foreach is a very powerful tool, since it acts as a general-purpose enumerator over many types. The tradeoff for this generalization is speed, and if you rely heavily on string iteration you should use a For loop instead."
oraz http://msdn.microsoft.com/en-us/library/ms998547 część Iterating and Looping "Use for instead of foreach in performance-critical code paths".
Właściwie to żeby nie powielać tego co już ludzie napisali dawno temu podam link do artykułu o wydajności różnych typów iterowania po List<T>:
http://diditwith.net/2006/10/05/PerformanceOfForeachVsListForEach.aspx

Oczywiście różnice uwidaczniają się przy dużych kolekcjach. Ale warto zdawać sobie z tego sprawę.

4

To, że foreach jest wolniejszy to raczej wiadomo od zawsze O_o. Jednak jest ważna kwestia o ile wolniejszy ?
Wygoda z używania foreach jest dość spora (przynajmniej dla mnie), zawsze to parę kliknięć w klawiaturę mniej. I myślę, że jeśli ktoś nie robi superwydajnych aplikacji np. nowoczesnych produkcji w 3D, to nie ma co sobie zaśmiecać głowy tym, czy użyć for, czy foreach

Nie podałeś wyników, mi się osobiście nie chce kompilować kodu. Ps. do testowania szybkości wykonywania metod itd, używa się klasy Stopwatch

0

albo ten prosty przykładzik:
http://www.codeproject.com/KB/cs/foreach.aspx - ale to chyba wyważanie otwartych drzwi

1

Wg mnie nie ma sensu zajmować się takimi pierdołami, jeżeli komuś zależy na wydajności to niech klepie kod w C++ z miliardem templejtów. Może się np okazać, że w .NET 8.0 foreach będzie np się sam wektoryzował na SSE (co przyniesie np 5x wzrost wydajności), a zwykły for nie i co wtedy zrobicie?

1

@Wibowit - no właśnie.
@emilklim - dzięki za linka
@tomkos - odejdź, piszesz mądrze ale ja tutaj chcę flame rozpętać a ty łagodzisz ;)

for (int i = 0; i < l1.Count; i++)
{ }

Ki diabeł za przeproszeniem (żeby nie napisać brzydziej)?

public static class Drain
{
    public static void Put<T>(T data) { }
}
            DateTime d1 = DateTime.UtcNow;
            for (int i = 0; i < l1.Count; i++)
            { Drain.Put(l1[i]); }
            DateTime d2 = DateTime.UtcNow;
            foreach (var x in l1)
            { Drain.Put(x); }
            DateTime d3 = DateTime.UtcNow;

I już for wykonuje się dla value typów dłuże o 33% (dla for - 12 ms, dla foreach - 9 ms). Jeśli zdążę, za chwilę dostaniesz kolejną listę rzeczy czemu twój test jest zły :P

Dalej - początkowo z zapału flejmowania pojechałem po artykule podlinkowanym przez emilkina, ale zrozumiałem że to błąd po tym jak go przeczytałem - jednak nie faworyzuje for-a.
Co więcej - skoro JIT jest w stanie do tego stopnia rozwniąć foreach to znaczy że jest może z nim zrobić wszystko. Łącznie z przeprowadzaniem doskonałych optymalizacji opierających się na wysokopoziomowej abstrakcji iterowania po danych. A for zawsze pozostanie tylko żałosnym forem, wykonującym jak tępy niewolnkik swoją pracę

(chyba mnie flame poniosło ;) )

1

A Javowcy sikają na takie problemy. Ten kod:

public class Main {

    public static void main(String[] args) {
        final long[] numbers = new long[100000000];
        for (int i = 0; i < numbers.length; i++) {
            numbers[i] = i * i + 5;
        }

        long x = 234553523525L;

        long time = System.currentTimeMillis();
        for (int i = 0; i < numbers.length; i++) {
            x += x * 7 + numbers[i] + 3;
        }
        System.out.println(System.currentTimeMillis() - time);
        time = System.currentTimeMillis();
        for (long i : numbers) {
            x += x * 7 + i + 4;
        }
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(x);
    }
}

Daje takie wyniki:

204
207
-8228431030235663118

Skoro nie widać różnicy to po co przepłacać :P

Okay, kod dla list:

import java.util.ArrayList;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        final List<Long> numbers = new ArrayList<Long>(100000000);
        for (int i = 0; i < 100000000; i++) {
            numbers.add(i * i + 5L);
        }

        long x = 234553523525L;

        long time = System.currentTimeMillis();
        for (int i = 0; i < numbers.size(); i++) {
            x += x * 7 + numbers.get(i) + 3;
        }
        System.out.println(System.currentTimeMillis() - time);
        time = System.currentTimeMillis();
        for (long i : numbers) {
            x += x * 7 + i + 4;
        }
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(x);
    }
}

Wynik:

518
510
-8228431030235663118

To samo. Te kilka milisekund to granica błędu statystycznego czy też czas potrzebny na odpalenie JITa.

Skoro w Javie różnicy nie ma, to w C# też pewnie niedługo nie będzie.

2

Wygrałem :>

Zastanowiłem się i doszedłem do wniosku że Drain jest tylko schowaniem problemu pod dywan. Gdybym naprawdę musiał (ciężko sobie wyobrazić bo sytuacja jest dalej nierealistyczna) wywołać metodę Put() każdym obiektem z listy to... co bym zrobił? No tak, Ofc...

DateTime d1 = DateTime.UtcNow;
for (int i = 0; i < l1.Count; i++)
{ Drain.Put(l1[i]); }
DateTime d2 = DateTime.UtcNow;
l1.ForEach((x) => Drain.Put(x));
DateTime d3 = DateTime.UtcNow;

For przegrywa na wszystkich frontach (wszędzie jest wolniejszy). Szach - Mat :D [Radość]

0

@msm z użyciem drain.put wersja release

int - value type
for : 2
foreach: 5
FooClass1 - reference type
for : 3
foreach: 12
DateTime - value type
for : 6
foreach: 12
string - refernece type, immutable type
for : 2
foreach: 12

nie chce mi wyjść gorzej dla for :)

Nie chodzi o to aby bezwzględnie nie używać foreach, ale żeby zdawać sobie sprawę jak działają pewne elementy .net. A mimo olbrzymiego postępu technologicznego coś takiego jak wydajność nadal musi zajmować uwagę programistów i w takim kontekście były moje wypowiedzi dotyczące niższej wydajności foreach.

1
MSM napisał(a)

l1.ForEach((x) => Drain.Put(x));

For przegrywa na wszystkich frontach (wszędzie jest wolniejszy). Szach - Mat :D [Radość]

Lol, MSM jednak się nie popisałeś!
Wiesz że foreach a List<T>.ForEach to kompletnie różne rzeczy?

public void ForEach(Action<T> action)
{
    if (action == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    for (int i = 0; i < this._size; i++)
    {
        action(this._items[i]);
    }
}
0

Hejka

tutaj tez byla dyskusja i okazuje sie ze wszystko zalezy gdzie sie uzywa tego foreach

http://www.codeguru.pl/frmThread.aspx?id=485944#485944

pzdr

1

@massther - jaka wersja frameworka?

Sprawdziłem dla release, ale chyba coś źle robię:

int - value type
for    : 1,0001
foreach: 0
FooClass1 - reference type
for    : 5,0003
foreach: 0
DateTime - value type
for    : 2,0001
foreach: 0
string - refernece type, immutable type
for    : 4,0002
foreach: 0

:D (serio, to nie fake)

edit:
No, niestety udao mi się uruchomić wersję release i faktycznie wychodzą wartości bardziej zbliżone do twoich:

int - value type
for    : 2,0002
foreach: 5,0002
FooClass1 - reference type
for    : 9,0005
foreach: 15,0008
DateTime - value type
for    : 3,0002
foreach: 7,0004
string - refernece type, immutable type
for    : 8,0005
foreach: 14,0008
0

framework 3.5
Ale powtórze - słowo kluczowe foreach, a List<T>.ForEach to kompletnie dwie różne sprawy! Jak otworzysz ostatni link w pierwszym poście i przewiniesz trochę w dół, to masz tam ładny wykresik, który pokazuje że List<T>.ForEach był wydajniejszy niż for, ale foreach poległo totalnie.

0
observer napisał(a)

tutaj tez byla dyskusja i okazuje sie ze wszystko zalezy gdzie sie uzywa tego foreach

http://www.codeguru.pl/frmThread.aspx?id=485944#485944

Przetestowałem podany kod w tej dyskusji i niestety też wypada gorzej od for, ale lepiej od standardowego enumeratora.

Oczywiście czasem przejrzystość kodu można przedkładać nad wydajność, ale powinny to być sytuacje wyjątkowe, a nie norma.

1

Oczywiście czasem przejrzystość kodu można przedkładać nad wydajność, ale powinny to być sytuacje wyjątkowe, a nie norma.

Sorry za częściowy OT, ale chwilowo jestem done jeśli chodzi o pomysły na pognębienie for...
Więc podczepię się pod czytelność:

istnieje pewien prosty algorytm sprawdzający czy liczba jest pierwszą - po prostu (z głowy, w połowie pseudokod):

pierwsza = true;
for (int i = 2; i < sqrt(liczba); i++)
    if (liczba % i == 0) { pierwsza = false; break; }

Sprawdzamy liczby od 2 do pierwiastka z liczby (największy możliwy dzielnik), i jeśli żadna nie dzieli to znaczy że mamy liczbę pierwszą.
I co z tego? Otóż Robert C. Martin w swojej książce przeprowadził refaktoryzację analizę pewnego programu. Był w nim analogiczny (albo podobny algorytm). I co z tego? Otóż zalecił usunąć sqrt. Usunąć! Zmieniając w ten sposób złożoność z O(sqrt(N)) na O(N) -> to tak jakby zmienić z O(N) na O(N^2)!
Czemu? Bo nie jest na pierwszy rzut oka, i jeśli ktoś nie zna algorytmu jasne po co tam to sqrt stoi. A może się okaże że będziemy pierwszość liczyć tylko kilka razy i to z małych liczb? Nie jesteśmy tego w stanie stwierdzić przed profilowaniem.
Szaleństwo? Może. Ale może jest w nim metoda?...

(A teraz wyobraż sobie co zrobiłby Martin po przeczytaniu o odgórnym używaniu fora dla tych kilku milisekund które może zyskasz a może nie...

2

Teraz wam koparki opadną.

Kod:

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

interface Function<T> {
    long perform(T parameter, long x);
}

class MyArray<T> {

    T[] array;
    long x;

    public MyArray(int size, Class<T> type, long x) {
        array = (T[]) Array.newInstance(type, size);
        this.x = x;
    }

    public void forEach(Function<T> function) {
        for (T element : array) {
            x = function.perform(element, x);
        }
    }
}

class Compute {
    int factor;
    final long constant;

    public Compute(int factor, long constant) {
        this.factor = factor;
        this.constant = constant;
    }

    public long compute(long parameter, long x) {
        return x * factor + parameter + constant;
    }
}

public class Main {

    public static void main(String[] args) {
        List<Long> numbers = new ArrayList<Long>(50000000);
        for (int i = 0; i < 50000000; i++) {
            numbers.add(i * i + 5L);
        }

        long x = 234553523525L;

        long time = System.currentTimeMillis();
        for (int i = 0; i < numbers.size(); i++) {
            x += x * 7 + numbers.get(i) + 3;
        }
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(x);
        x = 0;
        time = System.currentTimeMillis();
        for (long i : numbers) {
            x += x * 7 + i + 3;
        }
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(x);
        x = 0;
        numbers = null;
        MyArray<Long> myArray = new MyArray<Long>(50000000, Long.class, 234553523525L);
        for (int i = 0; i < 50000000; i++) {
            myArray.array[i] = i * i + 3L;
        }
        time = System.currentTimeMillis();
        myArray.forEach(new Function<Long>() {

            public long perform(Long parameter, long x) {
                return x * 8 + parameter + 5L;
            }
        });
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(myArray.x);
        myArray = null;
        myArray = new MyArray<Long>(50000000, Long.class, 234553523525L);
        for (int i = 0; i < 50000000; i++) {
            myArray.array[i] = i * i + 3L;
        }
        time = System.currentTimeMillis();
        myArray.forEach(new Function<Long>() {

            public long perform(Long parameter, long x) {
                return new Compute(8, 5).compute(parameter, x);
            }
        });
        System.out.println(System.currentTimeMillis() - time);
        System.out.println(myArray.x);
    }
}

Wynik:

276
-699150247503735895
274
-699150247503735895
248
-699150247503735895
266
-699150247503735895

Yeahhhhh!!! Escape Analysis w końcu działa (w Sun Java 6 Update 24) :) No i głęboka dewirtualizacja metod.

Jak widać zwykły for, który robi tylko trzy dodawania działa mniej więcej tak samo szybko jak pętla w której tworzymy sobie obiekt i przebijamy się przez 4 albo i więcej vtable.

Wy siszarpowcy se możecie tracić czas na optymalizacje forów na waszym lamerskim dotnecie :P

1

Wy siszarpowcy se możecie tracić czas na optymalizacje forów na waszym lamerskim dotnecie

Jeszcze zobaczysz :P

2

Szkoda, że to nie ja ukradłem to hasło deusa, bo przeniósłbym do perełek.

@massther - co Ty piłeś? No chyba, że faktycznie w pracy piszesz PUSTE pętle. Co Ty w ten sposób testujesz?

Kompilator wyrzuci pustą pętlę w kosmos, a dla iteratora (bo foreach to tylko cukier na iterator przecież!) konsekwetnie będzie wywoływał GetCurrent() i MoveNext(). To co dziwnego w tym, że pierwsze jest miliard razy szybsze od drugiego?
Do Twojego kodu wystarczy wstawić głupie string s = li[i].ToString(); czy odpowiednio string s = x.ToString(); aby różnica zniknęła. A w prostym (aczkolwiek realnym) kodzie listującym rekurencyjnie pliki z dysku mam zazwyczaj minimalną różnicę na rzecz foreach.

0

Od strony analitycznej, zarówno foreach, jak i for mają tę samą złożoność O(n).

0

Ciekawostka:
Foreach jest szybszy od nieoptymalnie napisanego for'a, a nie każdy pisze pętle for optymalnie.
Foreach przy iteracji list w sharepoint server 2007 w składnikach webpart jest dużo szybszy od zwykłej pętli for, jeśli for nie jest "idealny".
np. dla 36elementowej listy i 10 iteracji. Wyniki w w milisekundach:

##Foreach##
Total: 195,2576
First: 80,9484
Longest: 20,3121

##For##
Total: 13128,8633
First: 1312,4609
Longest: 1464,3591

##Foreach##
Total: 192,8305
First: 23,1601
Longest: 20,0915

##For##
Total: 12758,0603
First: 1293,0602
Longest: 1296,0965
private void ListFor()
        {
                TimeSpan totalTime;
                TimeSpan longest = new TimeSpan(0);
                TimeSpan first;
                int iterations = Int32.Parse(TBIter.Text);

                SPSite site = new SPSite(TBAddress.Text);
                SPWeb web = site.OpenWeb();
                SPFolder folder = web.GetFolder(TBAddress.Text);
                SPList list = web.Lists[folder.ParentListId];

                string test;

                Stopwatch sFirst = new Stopwatch();
                sFirst.Reset();

                sFirst.Start();
                for (int j = 0; j < list.Items.Count; j++)
                {
                    test = list.Items[j][0].ToString();
                }
                sFirst.Stop();
                first = sFirst.Elapsed;

                Stopwatch sTotal = new Stopwatch();
                sTotal.Reset();
                Stopwatch sIter = new Stopwatch();
                sIter.Reset();

                sTotal.Start();
                for (int i = 0; i < iterations; i++)
                {
                    sIter.Start();
                    for (int j = 0; j < list.Items.Count; j++)
                    {
                        test = list.Items[j][0].ToString();
                    }
                    sIter.Stop();
                    longest = longest > sIter.Elapsed ? longest : sIter.Elapsed;
                    sIter.Reset();
                }
                sTotal.Stop();
                totalTime = sTotal.Elapsed;

                LError.Text += "<br />";
                LError.Text += "##For##";
                LError.Text += "<br />";
                LError.Text += string.Format("Total: {0}", totalTime.TotalMilliseconds);
                LError.Text += "<br />";
                LError.Text += string.Format("First: {0}", first.TotalMilliseconds);
                LError.Text += "<br />";
                LError.Text += string.Format("Longest: {0}", longest.TotalMilliseconds);
        }

        private void ListForEach()
        {
            TimeSpan totalTime;
            TimeSpan longest = new TimeSpan(0);
            TimeSpan first;
            int iterations = Int32.Parse(TBIter.Text);

            SPSite site = new SPSite(TBAddress.Text);
            SPWeb web = site.OpenWeb();
            SPFolder folder = web.GetFolder(TBAddress.Text);
            SPList list = web.Lists[folder.ParentListId];

            string test;
            Stopwatch sFirst = new Stopwatch();
            sFirst.Reset();

            sFirst.Start();
            foreach (SPListItem item in list.Items)
            {
                test = item[0].ToString();
            }
            sFirst.Stop();
            first = sFirst.Elapsed;

            Stopwatch sTotal = new Stopwatch();
            sTotal.Reset();
            Stopwatch sIter = new Stopwatch();
            sIter.Reset();

            sTotal.Start();
            for (int i = 0; i < iterations; i++)
            {
                sIter.Start();
                foreach (SPListItem item in list.Items)
                {
                    test = item[0].ToString();
                }
                sIter.Stop();
                longest = longest > sIter.Elapsed ? longest : sIter.Elapsed;
                sIter.Reset();
            }
            sTotal.Stop();
            totalTime = sTotal.Elapsed;

            LError.Text += "<br />";
            LError.Text += "##Foreach##";
            LError.Text += "<br />";
            LError.Text += string.Format("Total: {0}", totalTime.TotalMilliseconds);
            LError.Text += "<br />";
            LError.Text += string.Format("First: {0}", first.TotalMilliseconds);
            LError.Text += "<br />";
            LError.Text += string.Format("Longest: {0}", longest.TotalMilliseconds);
        }

100 iteracji "for" MOSS2007 nie wytrzymał. Dla 90 było

##For##
Total: 115632,8938
First: 1311,6052
Longest: 1520,4013
##Foreach##
Total: 1779,4735
First: 24,5272
Longest: 24,944

po wyciągnięciu długości listy do pomocniczej zmiennej i używanie jej w warunku pętli wyniki dla for spadły o ok 50%, eg.

int len = list.Items.Count; 

For dla 10 i 100 iteracji

##For##
Total: 6697,928
First: 831,1699
Longest: 689,4777

##For##
Total: 67110,2434
First: 684,6103
Longest: 843,9248

##Foreach##
Total: 213,7504
First: 25,727
Longest: 25,7378

##Foreach##
Total: 2064,629
First: 28,0755
Longest: 27,1908

Ale dalej foreach miażdży for'a. Why?

dopiero poniższe działa poprawnie(

private void ListFor()
        {
                TimeSpan totalTime;
                TimeSpan longest = new TimeSpan(0);
                TimeSpan first;
                int iterations = Int32.Parse(TBIter.Text);

                SPSite site = new SPSite(TBAddress.Text);
                SPWeb web = site.OpenWeb();
                SPFolder folder = web.GetFolder(TBAddress.Text);
                SPList list = web.Lists[folder.ParentListId];

                string test;

                Stopwatch sFirst = new Stopwatch();
                sFirst.Reset();

                int len = list.Items.Count;
                SPListItemCollection col = list.Items;
                sFirst.Start();
                for (int j = 0; j < len; j++)
                {
                    test = col[j][0].ToString();
                }
                sFirst.Stop();
                first = sFirst.Elapsed;

                Stopwatch sTotal = new Stopwatch();
                sTotal.Reset();
                Stopwatch sIter = new Stopwatch();
                sIter.Reset();

                sTotal.Start();
                for (int i = 0; i < iterations; i++)
                {
                    sIter.Start();
                    for (int j = 0; j < len; j++)
                    {
                        test = col[j][0].ToString();
                    }
                    sIter.Stop();
                    longest = longest > sIter.Elapsed ? longest : sIter.Elapsed;
                    sIter.Reset();
                }
                sTotal.Stop();
                totalTime = sTotal.Elapsed;

                LError.Text += "<br />";
                LError.Text += "##For##";
                LError.Text += "<br />";
                LError.Text += string.Format("Total: {0}", totalTime.TotalMilliseconds);
                LError.Text += "<br />";
                LError.Text += string.Format("First: {0}", first.TotalMilliseconds);
                LError.Text += "<br />";
                LError.Text += string.Format("Longest: {0}", longest.TotalMilliseconds);
        }
0

jeszcze wyniki dla ostatniego(100 iteracji):

##For##
Total: 26,72
First: 21,6539
Longest: 0,3643
##Foreach##
Total: 2107,4417
First: 24,3958
Longest: 182,5767
##For##
Total: 26,7854
First: 19,1239
Longest: 0,3936
##Foreach##
Total: 2000,4084
First: 23,4724
Longest: 35,9417 
0

Ale głupoty - rozwód nad porównaniem piernika do wiatraka.

Przecież for i foreach to zupełnie dwie różne rzeczy w .NET (gdzie drugi to tylko cukierek składniowy, który używa interfejsów IEnumerable i IEnumerator - GetEnumerator(), Current i MoveNext(). Jak to się ma do pętli for?

Pewnie, że można wymyślić tysiąc przykładów, gdzie jedno jest wolniejsze od drugiego - ale po co?

Nie chodzi o to aby bezwzględnie nie używać foreach, ale żeby zdawać sobie sprawę jak działają pewne elementy .net. A mimo olbrzymiego postępu technologicznego coś takiego jak wydajność nadal musi zajmować uwagę programistów i w takim kontekście były moje wypowiedzi dotyczące niższej wydajności foreach.

Ta wydajność w 99,99% przypadków iteracji nad "normalną" kolekcją będzie na korzyść for.

(bo foreach to tylko cukier na iterator przecież!)

Programiści z Microsoftu użyli chyba słowo "iterator" do innego elementu tego języka: yield

http://msdn.microsoft.com/en-us/library/dscyy5s0%28v=vs.80%29.aspx

pierwsza = true;
for (int i = 2; i < sqrt(liczba); i++)
    if (liczba % i == 0) { pierwsza = false; break; }
    

Możesz wyjaśnić dlaczego akurat chciałeś pokazać ten przykład (rozumiem, że na niekorzyść pętli for?) Raczej oczywiste jest, że cała metoda iteracji będzie 10 razy wolniejsza niż sprawdzenie warunku dla każdej iteracji.

1

Ale głupoty - rozwód nad porównaniem piernika do wiatraka.

To ktoś to bierze na poważnie? ;)

Możesz wyjaśnić dlaczego akurat chciałeś pokazać ten przykład (rozumiem, że na niekorzyść pętli for?) Raczej oczywiste jest, że cała metoda iteracji będzie 10 razy wolniejsza niż sprawdzenie warunku dla każdej iteracji.

Akurat chodziło mi o pokazanie pewnego podejścia do wydajności - cytat:

Drugi komentarz [Dopisek by MSM: dotyczący powodu zastosowania sqrt] jest niemal na pewno niezbędny. Wyjaśnia powody zastosowania pierwiastka
jako ograniczenia pętli. Można sprawdzić, że żadna z prostych nazw zmiennych ani inna struktura
kodu nie pozwala na wyjaśnienie tego punktu. Z drugiej strony, użycie pierwiastka może być próżnością.
Czy faktycznie oszczędzam dużo czasu przez ograniczenie liczby iteracji do pierwiastka
liczby? Czy obliczenie pierwiastka nie zajmuje więcej czasu, niż uda się nam zaoszczędzić?
Warto o tym pomyśleć. Zastosowanie pierwiastka jako limitu pętli zadowala siedzącego we mnie
eksperta C i asemblera, ale nie jestem przekonany, że jest to warte czasu i energii osoby, która ma
za zadanie zrozumieć ten kod.

Jeszcze mały przykład który mi się niechcąco trochę rozrósł - wykorzystanie foreach do operowania na nieskończonych strukturach danych :]
Zastosowania niestety bezsensowne, ale nie mam natchnienia...

    class Integers : IEnumerable<int>
    {
        int start;
        int? end;
        int offset;

        public Integers(int start, int offset)
        {
            this.start = start;
            this.end = null;
            this.offset = offset;
        }

        public Integers(int start, int end, int offset)
        {
            this.start = start;
            this.end = end;
            this.offset = offset;
        }

        public IEnumerator<int> GetEnumerator()
        {
            int current = start;

            while (end == null || current < end)
            {
                yield return current;
                current += offset;
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    public static class IntExtension
    {
        public static int NumberSum(this int i) // nie chce mi się szukać jak to jest po angielsku...
        {
            int suma = 0;
            while (i > 0)
            {
                suma += i % 10;
                i /= 10;
            }

            return suma;
        }
    }

    class Fibs : IEnumerable<int> // fajniejszy przykład, nieskończony ciąg liczb fibbonaciego
    {
        public IEnumerator<int> GetEnumerator()
        {
            int a = 0;
            int b = 1;

            while(true)
            {
                yield return a;
                int c = a;
                a = b;
                b += c;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
           return GetEnumerator();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(20.NumberSum());

            Integers ints = new Integers(100, 2); 
            foreach (int i in ints) 
                // szukamy najmniejszej liczby większej od 100
                // której suma cyfr wynosi 15 i reszta z dzielenia przez
                // 21 wynosi 15
            {
                if (i.NumberSum() == 15 && i % 21 == 0)
                {
                    Console.WriteLine(i);
                    break;
                }
            }

            Fibs fibs = new Fibs();
            foreach (int fib in fibs)
                 // pierwsza liczba fibbonaciego o simie cyfr 17 i reszcie z dzielenia przez 6 == 5
            {
                if (fib.NumberSum() == 17 && fib % 6 == 5) // heh
                {
                    Console.WriteLine(fib);
                    break;
                }
            }

            Console.Read();
        }
    }
}
0

Taki nieskończony enumerator swój sens traci w momencie przekręcenia się zakresu.
Przy okazji, byłem ciekaw czy użycie tego w LINQ zadziała czy zwiesi program w nieskończonej pętli.

            Fibs fibs = new Fibs();
            var query = from fib in fibs
                        where fib.NumberSum() == 17 && fib % 6 == 5
                        select fib;
            Console.WriteLine(query.First());

o dziwo, działa — pod warunkiem że istnieje liczba spełniająca warunek. inaczej enumerator nigdy się nie skończy.

1 użytkowników online, w tym zalogowanych: 0, gości: 1