W jakim stopniu ogólne i metaprogramowanie przy użyciu szablonów C ++ jest przydatne w informatyce?

17

Język C ++ zapewnia ogólne programowanie i metaprogramowanie za pomocą szablonów. Techniki te znalazły zastosowanie w wielu dużych komputerowych pakietach naukowych (np. MPQC , LAMMPS , CGAL , Trilinos ). Ale co w rzeczywistości przyczynili się do obliczeń naukowych, które wykraczają poza nie-ogólne, niemetaliczne języki, takie jak C lub Fortran, pod względem całkowitego czasu opracowywania i użyteczności dla równej lub odpowiedniej wydajności?

Biorąc pod uwagę naukowe zadanie obliczeniowe, czy ogólne i metaprogramowanie za pomocą szablonów C ++ wykazało poprawę wydajności, ekspresji lub użyteczności mierzoną za pomocą dobrze zrozumiałych testów porównawczych (linii kodu, nakładu pracy itp.)? Odpowiednio, jakie ryzyko wiąże się ze stosowaniem szablonów C ++ do generycznego i metaprogramowania?

Oddech śmierci
źródło
Obawiam się, że to pytanie może być zbyt otwarte na opinie, ale chcę zobaczyć, co mówią ludzie w społeczności.
Geoff Oxberry
1
Całkowicie usunąłem wykolejenie komentarza. Jeśli chcesz, aby został on opublikowany na czacie lub meta, cieszę się, że mogę to zrobić. Zobacz mój meta tutaj .
Aron Ahmadia
3
Zapoznaj się również z pokrewnym pytaniem na temat tego, kiedy używać, a kiedy unikać szablonów wyrażeń .
Aron Ahmadia
Nie uważam za słuszne stwierdzenie, że LAMMPS korzysta z szablonów lub metaprogramowania. LAMMPS jest kodem obiektowym, który przez większość czasu bardzo przypomina Fortran. Nie sądzę, że MPQC ma również dużo szablonów, ale jest silnie zorientowany obiektowo i polimorficzny.
Jeff
1
OpenFOAM intensywnie wykorzystuje szablony i inne funkcje C ++.
Dohn Joe

Odpowiedzi:

14

Wydaje mi się, że metaprogramowanie szablonów okazało się w praktyce niemożliwe do użycia - kompiluje się zbyt wolno, a otrzymywane komunikaty o błędach są po prostu niemożliwe do odczytania. Bariera wejścia dla nowoprzybyłych jest również zbyt wysoka w przypadku korzystania z metaprogramowania.

Oczywiście, ogólne programowanie to zupełnie inna kwestia, o czym świadczą Trilinos, deal.II (moja własna biblioteka), DUNE i wiele innych bibliotek - wyrażanie tej samej koncepcji działającej na różnych typach danych jest czymś oczywistym, i społeczność w dużej mierze zaakceptowała to, o ile pozostaje w granicach, które pozwalają uniknąć problemów związanych z metaprogramowaniem. Myślę, że ogólne programowanie kwalifikuje się jako oczywisty sukces.

Oczywiście żaden z tych tematów nie jest natychmiast związany z OOP. Powiedziałbym, że OOP jest powszechnie akceptowane przez naukową społeczność komputerową. Nawet mniej niż ogólne programowanie, nie jest to temat debaty: każda udana biblioteka napisana w ciągu ostatnich 15 lat (napisana w C ++, C lub Fortran) korzysta z technik OOP.

Wolfgang Bangerth
źródło
4
tmp może być trudny dla początkujących użytkowników, ale często robi się to bardzo dobrze w bibliotece. Jest to jedna z tych technik, które mogą naprawdę zmniejszyć ilość kodu, ale naprawdę musisz wiedzieć, co robisz. Jeśli mi nie wierzysz, przeczytaj źródło Eigen lub Elemental. Piękny kod, który bez szablonów byłby prawie niemożliwy.
aterrel
5
Jasne, to technika o wartości. Ale jest trudny w utrzymaniu i często jest trudny w użyciu, jeśli jest narażony na interfejs zewnętrzny. To powiedziawszy, myślę, że jednym z powodów, dla których TMP nie odniósł sukcesu, którego ludzie początkowo mogli się spodziewać, jest to, że kompilatory stały się bardzo dobre. TMP żyje z faktu, że wiele rzeczy jest znanych w czasie kompilacji, a następnie można je propagować jako stałe do rzeczywistego kodu. Ale kompilatory stały się całkiem dobre we wprowadzaniu, klonowaniu funkcji itp., Więc znaczną część korzyści można dziś uzyskać za pomocą „normalnego” programowania.
Wolfgang Bangerth
15

Podam przykład oparty na doświadczeniu. Większość bibliotek, z których korzystam na co dzień, korzysta w pewien sposób z OOP. OOP jest w stanie ukryć złożoność wymaganą dla wielu domen, nie jest to mechanizm, który naprawdę pomaga w wydajności. Może się zdarzyć, że biblioteka będzie w stanie wykorzystywać określone optymalizacje oparte na hierarchii obiektów, ale w większości chodzi o ukrywanie złożoności przed użytkownikiem. Spójrz w górę Wzory projektowe, są to mechanizmy często stosowane w celu ukrycia tej złożoności.

Weźmy PETSc jako przykład. PETSc wykorzystuje model inspektora / modułu wykonawczego OOP, w którym dowolny z jego algorytmów analizuje dostępne procedury w danym obiekcie i wybiera, które wykonać, aby wykonać procedurę. Pozwala to użytkownikowi rozdzielić problemy, na przykład działanie matrycy może obejmować dowolny rodzaj zablokowanej lub zoptymalizowanej procedury i może być skutecznie wykorzystywane przez wiele iteracyjnych solverów. Dając użytkownikowi możliwość określania własnych typów danych i ocen, przyspiesza kilka ważnych procedur, a także udostępnia całą funkcjonalność biblioteki.

Innym przykładem, który podam, jest FEniCS i deal.II. Obie biblioteki używają OOP do uogólnienia na dużą liczbę metod elementów skończonych. Zarówno wszystko, od typu elementu, kolejności elementów, reprezentacji kwadratury itp. Jest wymienne. Chociaż obie te biblioteki są „wolniejsze” niż niektóre kody FEM o specjalnej strukturze, są w stanie rozwiązać wiele różnych problemów przy dużej złożoności MES nieznanej użytkownikowi.

Mój ostatni przykład to Żywiołak. Elemental to nowa gęsta biblioteka algebry liniowej, która podniosła trudność zarządzania komunikatorami MPI i lokalizacją danych w bardzo prostej konstrukcji językowej. W rezultacie, jeśli masz kod szeregowy FLAME, zmieniając typy danych, możesz również mieć kod równoległy za pośrednictwem elementu. Jeszcze bardziej interesujące możesz grać z dystrybucją danych, ustawiając dystrybucję równą innej.

OOP należy traktować jako sposób zarządzania złożonością, a nie paradygmat konkurowania z ręcznym walcowaniem. Również robienie tego źle spowoduje dużo narzutu, dlatego należy stale mierzyć czas i aktualizować mechanizmy, z których korzystają.

aterrel
źródło
3

Jakie funkcje językowe OOProbią dla obliczeń naukowych, tworzą bardziej zwięzłe instrukcje kodu, co pomaga w lepszym zrozumieniu i korzystaniu z kodu. Na przykład FFTprocedury muszą zawierać dużą liczbę argumentów dla każdego wywołania funkcji, co powoduje, że kod jest kłopotliwy.

Za pomocą instrukcji modulelub classinstrukcji można przekazać tylko to, co jest potrzebne w momencie wywołania, ponieważ pozostałe argumenty dotyczą konfiguracji problemu (tj. Rozmiaru tablic i współczynników).

Z mojego doświadczenia SUBROUTINEwynika , że miałem wywołania z 55 argumentami (in & out) i zredukowałem to do 5, dzięki czemu kod jest lepszy.

To jest wartość.

ja72
źródło
3

Jestem zdecydowanym zwolennikiem programowania ogólnego i metaprogramowania dla obliczeń naukowych. Właściwie tworzę bibliotekę wolnego oprogramowania C ++ dla metod Galerkina opartą na tych technikach o nazwie Feel ++ (http://www.feelpp.org), która stale nabiera tempa. To prawda, że ​​nadal występują trudności, takie jak wolne czasy kompilacji i że krzywa uczenia się może być stroma, jeśli chce się zrozumieć, co dzieje się za sceną. Jest to jednak niezwykle interesujące i oszałamiające. Jeśli zrobisz to na poziomie biblioteki i ukryjesz złożoność języka specyficznego dla domeny, otrzymasz niezwykle potężne narzędzie. Dysponujemy bardzo szeroką gamą metod stosowania i porównywania. Jest to świetny do nauczania celów obliczeń naukowych, do badań i nowych metod numerycznych, do zastosowań na dużą skalę, dobrze, pracujemy nad tym, ale jak dotąd tak dobrze, możemy już zrobić kilka fajnych rzeczy. Korzystają z niego inżynierowie, fizycy i matematycy: większość z nich używa języka do formułowania wariacyjnego i są z niego zadowoleni. Patrząc na niektóre formulacje, którymi manipulują nasi koledzy z fizyków, nie chciałbym, aby były wykonywane „ręcznie” bez wysokiego poziomu języka opisującego formułę wariacyjną. Osobiście uważam, że te „techniki” lub „paradygmaty” są teraz niezbędne do rozwiązania złożoności naukowego kodu komputerowego, z koniecznością pomnożenia rozmiaru kodu przez ogromny czynnik. Prawdopodobnie istnieje potrzeba poprawy obsługi metaprogramowania w C ++, ale jest ona już w dobrej formie, zwłaszcza od C ++ 11.

Christophe Prud'homme
źródło
2

Możesz znaleźć artykuł http://arxiv.org/abs/1104.1729 odpowiedni dla twojego pytania. Omówiono szablony wyrażeń (szczególne zastosowanie metaprogramowania szablonów stosowanych w kodzie naukowym) z perspektywy wydajności.

Juan M. Bello-Rivas
źródło
Ten papier doprowadza mnie do szaleństwa. Porównaj najszybszego zwykłego Fortrana z MKL, a on również straci. Ręcznie dostrojony montaż nie jest czymś, do czego się aspiruje, jest tym, co robisz, gdy liczy się każda nanosekunda i może być ponownie użyty przez bardzo dużą liczbę ludzi.
aterrel
@aterrel: Właśnie o tym zastanawiam się. Wiedząc, że będziesz musiał przeprowadzić optymalizację ręki jako ostatni etap rozwoju, jakiego języka użyjesz jako podstawy przed ostatnim etapem? Czy mamy twarde dane, które sugerują, który język wybrać?
Deathbreath
9
@Deathbreath: Powtórzę odpowiedź, którą udzieliłem również w kilku innych wątkach - ogólnie rzecz biorąc, przestrajanie ostatniej szybkości kodu jest czymś, co robisz bardzo rzadko. Ale cały czas programujesz algorytmy wysokiego poziomu. Wybierz język, który pozwala szybko robić duże rzeczy. Zawsze można w jakiś sposób uwzględnić elementy niskiego poziomu, ale to nie powinno decydować o wyborze języka programowania.
Wolfgang Bangerth
0

Szablony są bardzo dobre w usuwaniu kontroli typu / domeny w czasie wykonywania. Można to załatwić w czasie kompilacji. Teoretycznie może to zwiększyć wydajność w porównaniu z tym samym typem implementacji w C lub Fortran, gdzie sprawdzanie typu można wykonać tylko w czasie wykonywania - kontrole są implementowane w kodzie źródłowym. Można jednak osiągnąć te same wyniki w C przy użyciu opcji prekompilatora, ale muszą one być wykonane ręcznie, w przeciwieństwie do szablonów.

Jednak szablony mogą również generować znaczne koszty ogólne. Często mogą tworzyć nadęty kod, który może wpływać na użycie pamięci podręcznej instrukcji. Co więcej, podejścia ogólne mogą często wiązać kompilator podczas optymalizacji - nie zawsze jest to proste do analizy kodu, gdy stosuje się podejścia ogólne. Jest to zawsze problem z automatyzacją - w tym optymalizacją kompilatora - bardzo często kod wyjściowy nie jest przyjazny dla pamięci podręcznej.

Zalety sprawdzania typu / domeny, choć z pewnością bezpieczniejsze, są jedyną prawdziwą korzyścią, jaką widzę pod względem wydajności, i są one zazwyczaj niezauważalne. Ale jak mówię, ogólny efekt może być negatywny w zależności od tego, co robisz. Dlatego często lepiej jest ręcznie zoptymalizować kod tam, gdzie występują znaczne wąskie gardła.

cdcdcd
źródło