Załóżmy, że pracuję nad kodem naukowym w C ++. W niedawnej dyskusji z kolegą argumentowano, że szablony wyrażeń mogą być bardzo złe, potencjalnie czyniąc oprogramowanie kompilowalnym tylko w niektórych wersjach gcc. Podobno problem ten wpłynął na kilka kodów naukowych, jak wspomniano w podtytułach tej parodii Downfall . (Są to jedyne znane mi przykłady, stąd link).
Jednak inne osoby twierdziły, że szablony wyrażeń są przydatne, ponieważ mogą przynieść wzrost wydajności, jak w tym artykule w SIAM Journal of Scientific Computing , unikając przechowywania wyników pośrednich w zmiennych tymczasowych.
Nie znam się dużo na metaprogramowaniu szablonów w C ++, ale wiem, że jest to jedno podejście stosowane w automatycznym różnicowaniu i arytmetyki przedziałowej, i tak oto wdałem się w dyskusję na temat szablonów wyrażeń. Biorąc pod uwagę zarówno potencjalne zalety w wydajności, jak i potencjalne wady w utrzymaniu (jeśli to nawet właściwe słowo), kiedy powinienem używać szablonów wyrażeń C ++ w informatyce i kiedy powinienem ich unikać?
źródło
Odpowiedzi:
Mój problem z szablonami wyrażeń polega na tym, że są one bardzo nieszczelną abstrakcją. Dużo pracy spędzasz na pisaniu bardzo skomplikowanego kodu, aby wykonać proste zadanie z ładniejszą składnią. Ale jeśli chcesz zmienić algorytm, musisz zadzierać z brudnym kodem, a jeśli wpadniesz na różne typy lub składnię, otrzymasz całkowicie niezrozumiałe komunikaty o błędach. Jeśli Twoja aplikacja doskonale odwzorowuje się na bibliotekę opartą na szablonach wyrażeń, warto rozważyć, ale jeśli nie jesteś pewien, polecam po prostu napisać normalny kod. Jasne, kod wysokiego poziomu jest mniej ładny, ale możesz po prostu zrobić to, co trzeba. Korzyścią jest to, że czas kompilacji i rozmiary plików binarnych znacznie spadną i nie będziesz musiał radzić sobie z ogromną zmiennością wydajności dzięki wyborowi kompilatora i flagi kompilacji.
źródło
Inni komentowali kwestię tego, jak trudno jest pisać programy ET, a także złożoność zrozumienia komunikatów o błędach. Pozwólcie mi skomentować kwestię kompilatorów: Prawdą jest, że jakiś czas temu jednym z dużych problemów było znalezienie kompilatora, który jest wystarczająco zgodny ze standardem C ++, aby wszystko działało i było przenośne. W rezultacie znaleźliśmy wiele błędów - mam na imię 2-300 raportów o błędach, rozpowszechnianych na gcc, Intel icc, IBM xlC i Portland's pgicc. W związku z tym skrypt konfiguracyjny deal.II jest repozytorium dużej liczby testów błędów kompilatora, głównie w zakresie szablonów, deklaracji znajomych, przestrzeni nazw itp.
Okazuje się jednak, że twórcy kompilatorów naprawdę dobrze sobie poradzili: dzisiaj gcc i icc przeszły wszystkie nasze testy i łatwo jest napisać kod, który jest przenośny między nimi. Powiedziałbym, że PGI nie jest daleko w tyle, ale ma wiele dziwactw, które wydają się nie ustępować przez lata. Z drugiej strony xlC to zupełnie inna historia - naprawiają błąd co 6 miesięcy, ale pomimo przesyłania raportów o błędach od lat, postęp jest bardzo powolny, a xlC nigdy nie był w stanie skompilować oferty.
Oznacza to wszystko: jeśli będziesz trzymać się dwóch dużych kompilatorów, możesz spodziewać się, że działają one tylko dzisiaj. Ponieważ większość dzisiejszych komputerów i systemów operacyjnych zazwyczaj ma co najmniej jeden z nich, to wystarczy. Jedyną platformą, na której jest trudniej, jest BlueGene, gdzie kompilatorem systemowym jest zazwyczaj xlC ze wszystkimi jego błędami.
źródło
Dawno temu eksperymentowałem trochę z ET, kiedy, jak wspomniałeś, kompilatory wciąż miały z nimi problemy. Użyłem biblioteki blitz do algebry liniowej w moim kodzie. Problem polegał na uzyskaniu dobrego kompilatora, a ponieważ nie jestem doskonałym programistą C ++, interpretuję komunikatów o błędach kompilatora. Ten ostatni był po prostu niemożliwy do zarządzania. Kompilator generuje średnio około 1000 wierszy komunikatów o błędach. W żaden sposób nie byłem w stanie szybko znaleźć mojego błędu programistycznego.
Więcej informacji można znaleźć na stronie oonumerycznej (odbywają się dwa warsztaty ET).
Ale trzymałbym się od nich z daleka ...
źródło
Problem zaczyna się już od terminu „szablony wyrażeń (ET)”. Nie wiem, czy istnieje na to dokładna definicja. Ale w powszechnym użyciu w jakiś sposób łączy „sposób kodowania wyrażeń algebry liniowej” i „sposób obliczania”. Na przykład:
Kodujesz operację wektorową
I jest obliczany przez pętlę
Moim zdaniem są to dwie różne rzeczy i należy je oddzielić: (1) to interfejs i (2) jedna możliwa implementacja. Mam na myśli, że jest to powszechna praktyka w programowaniu. Pewnie (2) może być dobrą domyślną implementacją, ale ogólnie chcę móc korzystać ze specjalistycznej, dedykowanej implementacji. Na przykład chcę, aby ta funkcja była jak
zostań wywołany, gdy koduję (1). Może (3) po prostu używa wewnętrznie pętli jak w (2). Jednak w zależności od rozmiaru wektora inne implementacje mogą być bardziej wydajne. W każdym razie, jakiś ekspert od wysokiej wydajności może wdrożyć i dostroić (3) w jak największym stopniu. Jeśli więc (1) nie można zmapować na wywołanie (3), raczej unikam cukru syntaktycznego (1) i od razu wywołuję (3).
To, co opisuję, nie jest niczym nowym. Wręcz przeciwnie, to jest idea stojąca za BLAS / LPACK:
Jeśli zakres BLAS nie jest wystarczający (np. Nie zapewnia funkcji takiej jak (3)), wówczas można rozszerzyć zakres BLAS. Tak więc ten dinozaur z lat 60. i 70. dzięki swojemu narzędziu z epoki kamiennej dokonuje czystego i ortogonalnego rozdziału interfejsu i implementacji. To zabawne, że (większość) numerycznych bibliotek C ++ nie osiąga tego poziomu jakości oprogramowania. Chociaż sam język programowania jest o wiele bardziej wyrafinowany. Nic więc dziwnego, że BLAS / LAPACK wciąż żyje i aktywnie się rozwija.
Więc moim zdaniem ET nie są złe same w sobie. Jednak sposób, w jaki są one powszechnie stosowane w bibliotekach numerycznych C ++, przyniósł im bardzo złą reputację w naukowych kręgach komputerowych.
źródło