Perfekt für Loop

Heute ist das Format eines Artikels für mich ungewöhnlich: Ich stelle dem Publikum lieber eine Frage, als ein fertiges Rezept zu teilen. Ich schlage jedoch auch ein Rezept vor, um eine Diskussion einzuleiten. Also werden wir heute über das Gefühl der Schönheit sprechen.



Ich schreibe schon seit einiger Zeit Code und es kommt einfach so vor, dass er fast immer in C ++ ist. Ich kann nicht einmal abschätzen, wie oft ich eine solche Konstruktion geschrieben habe:



for (int i=0; i<size; i++) {
    [...]
}


Obwohl warum ich nicht kann, kann ich wirklich:



find . \( -name \*.h -o -name \*.cpp \) -exec grep -H "for (" {} \; | wc -l
43641


Unser aktuelles Projekt enthält 43.000 Zyklen. Ich bin nicht der einzige, der das Projekt sieht, aber das Team ist klein und mein Projekt ist nicht das erste (und ich hoffe nicht das letzte), daher wird es als grobe Schätzung verwendet. Wie forgut ist diese Loop-Aufnahme ? Tatsächlich ist es nicht einmal wichtig , wie oft ich den Zyklus geschrieben habe , sondern wie oft ich den Zyklus gelesen habe (siehe Debugging und Codeüberprüfung). Und hier sprechen wir offensichtlich von Millionen.



Bei KPDV wird ein Knoten als "Perfektionsschleife" bezeichnet .



Bild



Was ist der perfekte Zyklus?



Was ist das Problem?



; , , , . , :



for (int iter=0; iter<nb_iter; iter++) {          // some iterative computation
    for (int c=0; c<mesh.cells.nb(); c++)         // loop through all tetrahedra
        for (int lv0=0; lv0<4; lv0++)             // for every pair of
            for (int lv1 = lv0+1; lv1<4; lv1++)   // vertices in the tet
                for (int d=0; d<3; d++) {         // do stuff for each of 3 dimensions
                    nlRowScaling(weight);
                    nlBegin(NL_ROW);
                    nlCoefficient(mesh.cells.vertex(c, lv0)*3 + d,  1);
                    nlCoefficient(mesh.cells.vertex(c, lv1)*3 + d, -1);
                    nlEnd(NL_ROW);
                }
    [...]
}


, , . , , --, .



; . ( ) , for (int i=0; i<size; i++) — : for , .



for(;;), : , . , 0 size-1, . , ? , — .



c++11 , :



#define FOR(I,UPPERBND) for(int I = 0; I<int(UPPERBND); ++I)


:



FOR(iter, nb_iter) {
    FOR(c, mesh.cells.nb())
        FOR(lv0, 4)
            for (int lv1 = lv0+1; lv1<4; lv1++)
                FOR(d, 3) {
                    nlRowScaling(weight);
                    nlBegin(NL_ROW);
                    nlCoefficient(mesh.cells.vertex(c, lv0)*3 + d,  1);
                    nlCoefficient(mesh.cells.vertex(c, lv1)*3 + d, -1);
                    nlEnd(NL_ROW);
                }
    [...]
}


, for (;;), , (, , ). FOR(,) 0 n-1 - . , , , (. ), (, , ).



, , , : " , ?"



11 , range for



? , . , :



for i in range(n):
    print(i)


, range() , . c++11 !



#include <iostream>
int main() {
    int range[] = {0,1,2,3,4,5};
    for (int i : range) {
        std::cerr << i;
    }
}


, , . C++ !



range(int n):



#include <iostream>

constexpr auto range(int n) {
    struct iterator {
        int i;
        void operator++() { ++i; }
        bool operator!=(const iterator& rhs) const { return i != rhs.i; }
        const int &operator*() const { return i; }
    };
    struct wrapper {
        int n;
        auto begin() { return iterator{0}; }
        auto end()   { return iterator{n}; }
    };
    return wrapper{n};
}

int main() {
    for (int i: range(13)) {
        std::cerr << i;
    }
    return 0;
}


, int vs size_t, . gcc 10.2 -std=c++17 -Wall -Wextra -pedantic -O1, ( ):



[...]
.L2:
        mov     esi, ebx
        mov     edi, OFFSET FLAT:_ZSt4cerr
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        add     ebx, 1
        cmp     ebx, 13
        jne     .L2
[...]


, , for (int i=0; i<13; i++).



, for (int i: range(n)) , FOR(,), , .



: enumerate



Range for c++11 . , , , :



#include <vector>
#include <iostream>

struct vec3 {  double x,y,z;  };

int main() {
    std::vector<vec3> points = {{6,5,8},{1,2,3},{7,3,7}};
    for (vec3 &p: points) {
        std::cerr << p.x;
    }
    return 0;
}


for (vec3 &p: points) , , . , ? , . , , , :



    std::vector<vec3> points = {{6,5,8},{1,2,3},{7,3,7}};
    std::vector<double> weights = {4,6,9};
    int i = 0;
    for (vec3 &p: points) {
        std::cerr << p.x << weights[i++];
    }


:



asm
.L2:
        movsd   xmm0, QWORD PTR [r13+0]
        mov     edi, OFFSET FLAT:_ZSt4cerr
        call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<double>(double)
        movsd   xmm0, QWORD PTR [rbp+0]
        mov     rdi, rax
        call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<double>(double)
        add     rbp, 8
        add     r13, 24
        cmp     r14, rbp
        jne     .L2


, , , , , c++17 structural binding!



, :



#include <vector>
#include <iostream>
#include "range.h"

struct vec3 {
    double x,y,z;
};

int main() {
    std::vector<vec3> points = {{6,5,8},{1,2,3},{7,3,7}};
    std::vector<double> weights = {4,6,9};

    for (auto [i, p]: enumerate(points)) {
        std::cerr << p.x << weights[i];
    }

    return 0;
}


enumerate() :



range.h
#ifndef __RANGE_H__
#define __RANGE_H__

#include <tuple>
#include <utility>
#include <iterator>

constexpr auto range(int n) {
    struct iterator {
        int i;
        void operator++() { ++i; }
        bool operator!=(const iterator& rhs) const { return i != rhs.i; }
        const int &operator*() const { return i; }
    };
    struct wrapper {
        int n;
        auto begin() { return iterator{0}; }
        auto end()   { return iterator{n}; }
    };
    return wrapper{n};
}

template <typename T> constexpr auto enumerate(T && iterable) {
    struct iterator {
        int i;
        typedef decltype(std::begin(std::declval<T>())) iterator_type;
        iterator_type iter;
        bool operator!=(const iterator& rhs) const { return iter != rhs.iter; }
        void operator++() { ++i; ++iter; }
        auto operator*() const { return std::tie(i, *iter); }
    };
    struct wrapper {
        T iterable;
        auto begin() { return iterator{0, std::begin(iterable)}; }
        auto end()   { return iterator{0, std::end  (iterable)}; }
    };
    return wrapper{std::forward<T>(iterable)};
}

#endif // __RANGE_H__


-std=c++17 -Wall -Wextra -pedantic -O2 ( ):



ASM
.L14:
        movsd   xmm0, QWORD PTR [rbx]
        mov     edi, OFFSET FLAT:_ZSt4cerr
        call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<double>(double)
        mov     rdi, rax
        mov     rax, QWORD PTR [rsp+32]
        movsd   xmm0, QWORD PTR [rax+rbp]
        call    std::basic_ostream<char, std::char_traits<char> >& std::basic_ostream<char, std::char_traits<char> >::_M_insert<double>(double)
        add     rbx, 24
        add     rbp, 8
        cmp     r12, rbx
        jne     .L14


Und wieder entfernte der Compiler den Wrapper sauber (dafür war es jedoch notwendig, die Optimierungsstufe von -O1auf zu erhöhen -O2).

Übrigens erschien es in c ++ 20 std::ranges, was das Schreiben einer solchen Funktion noch einfacher macht, aber ich bin noch nicht bereit, auf diesen Standard umzusteigen.



Frage an das Publikum



Was sollte Ihrer Meinung nach der perfekte Zyklus im Jahr 2020 sein? Lehre mich!



Wenn Sie diese Frage noch nicht gestellt haben, kopieren Sie die Header-Datei in Ihr Haustierprojekt range.hund versuchen Sie, sie mindestens einige Tage lang zu verwenden.




All Articles