Kompilowanie i optymalizacja dla różnych architektur docelowych

Podsumowanie: chcę skorzystać z optymalizacji kompilatorów i zestawów instrukcji procesora, ale nadal mam przenośną aplikację (działającą na różnych procesorach). Normalnie mógłbym skompilować 5 razy i pozwolić użytkownikowi wybrać odpowiedni do uruchomienia.

Moje pytanie brzmi: Jak mogę to zautomatyzować, aby procesor był wykrywany w czasie wykonywania i wykonywany był odpowiedni plik wykonywalny bez konieczności wybierania go przez użytkownika?


Mam aplikację z wieloma obliczenia matematyczne niskiego poziomu. Obliczenia te będą zazwyczaj działać przez długi czas.

Chciałbym skorzystać z jak największej optymalizacji, najlepiej również (nie zawsze wspieranych) zestawów instrukcji. Z drugiej strony chciałbym, aby moja aplikacja była przenośna i łatwa w użyciu(więc nie chciałbym kompilować 5 różnych wersji i pozwolić użytkownikowi wybrać).

Czy jest możliwość skompilowania 5 różnych wersji mojego kodu i uruchomienia dynamicznie najbardziej zoptymalizowanego wersja, która jest możliwa w czasie realizacji? Z 5 różnymi wersjami mam na myśli różne zestawy instrukcji i różne optymalizacje dla procesorów.

Nie obchodzi mnie wielkość aplikacji.

W tej chwili używam gcc na Linuksie( mój kod jest w C++), ale interesuje mnie to również dla kompilatora Intela i dla kompilatora MinGW do kompilacji do Windows.

Plik wykonywalny nie musi być w stanie działać na różnych systemach operacyjnych, ale najlepiej tam byłoby coś możliwe z automatycznym wyborem 32 bitów i 64 bitów, jak również.

Edit: proszę podać jasne wskazówki, jak to zrobić, najlepiej z małymi przykładami kodu lub linkami do wyjaśnień. Z mojego punktu widzenia potrzebuję super generycznego rozwiązania, które ma zastosowanie w każdym losowym projekcie C++, który mam później.

Edit wyznaczyłem nagrodę dla Shuggycouka, miał wiele wskazówek, na które trzeba uważać. Chciałbym podzielić to między wiele odpowiedzi, ale to to niemożliwe. Nie mam jeszcze tego zaimplementowanego, więc pytanie jest wciąż "otwarte"! Proszę nadal dodawać i / lub poprawiać odpowiedzi, mimo że nie ma już nagrody.

Dzięki wszystkim!

Author: Georg Fritzsche, 2009-08-19

8 answers

Jeśli chcesz, aby to czysto pracować na Windows i w pełni wykorzystać w platformach 64bit obsługujących dodatkowe 1. Miejsce i 2. rejestry (prawdopodobnie bardziej przydatne dla ciebie) musisz mieć co najmniej oddzielny proces dla 64-bitowych.

Można to osiągnąć poprzez posiadanie oddzielnego pliku wykonywalnego z odpowiednim nagłówkiem PE64. Po prostu użycie CreateProcess uruchomi to jako odpowiedni bit (chyba że uruchomiony plik wykonywalny znajduje się w jakimś przekierowanym miejscu, nie ma potrzeby WOW64 Przekierowanie folderu

Biorąc pod uwagę to ograniczenie w systemie windows, jest prawdopodobne, że po prostu' łączenie ' z odpowiednim programem wykonywalnym będzie najprostszą opcją dla wszystkich różnych opcji, a także ułatwi testowanie poszczególnych opcji.

Oznacza to również, że "główny" plik wykonywalny może być całkowicie oddzielony w zależności od docelowego systemu operacyjnego (ponieważ wykrywanie możliwości procesora/systemu operacyjnego jest z natury bardzo specyficzne dla systemu operacyjnego), a następnie wykonaj większość reszty Twój kod jako udostępnione obiekty / biblioteki DLL. Możesz również "udostępniać" te same pliki dla dwóch różnych architektur, jeśli obecnie nie uważasz, że istnieje jakikolwiek sens korzystania z różnych możliwości.

Sugerowaĺ 'bym, Ĺźe gĹ' Ăłwny program wykonywalny moĹźe byÄ ‡ zmuszony do dokonania okreĹ "lonego wyboru, wiÄ ™ c moĹźna zobaczyć, co siÄ ™ dzieje z "mniejszymi" wersjami na bardziej wydajnej maszynie (lub jakie bĹ 'Ä ™ dy pojawiajÄ ... siÄ™, jeĹ" li spróbujesz czegoĹ " innego).

Inne możliwości tego modelu są:

  • statyczne łączenie z różnymi wersjami standardowych środowisk uruchomieniowych (dla tych z zabezpieczeniem wątku/bez) i używanie ich odpowiednio, jeśli pracujesz bez żadnych możliwości SMP / SMT.
  • wykrywa, czy istnieje wiele rdzeni i czy są one prawdziwe lub hyper threading (także czy system operacyjny wie, jak harmonogram skutecznie w tych przypadkach)
  • sprawdzanie wydajności takich rzeczy jak timer systemowy/timery o wysokiej wydajności i korzystanie z kodu zoptymalizowanego do to zachowanie, powiedzmy, jeśli robisz coś, gdzie szukasz określonej ilości czasu, aby wygasnąć, a tym samym możesz poznać swoją najlepszą możliwą ziarnistość.
  • jeśli chcesz zoptymalizować wybór kodu na podstawie rozmiaru pamięci podręcznej/innego obciążenia na pudełku. Jeśli używasz rozwijanych pętli, bardziej agresywne opcje rozwijania mogą zależeć od posiadania pewnej ilości pamięci podręcznej poziomu 1/2.
  • kompilowanie warunkowo do użycia podwaja / pływaki w zależności od architektury. Mniej ważne na sprzęcie Intela, ale jeśli targetujesz niektóre procesory ARM, niektóre mają rzeczywistą obsługę sprzętu zmiennoprzecinkowego, a inne wymagają emulacji. Optymalny kod zmieniłby się znacznie, nawet w takim stopniu, w jakim używasz kompilacji warunkowej, a nie kompilatora optymalizującego(1).
  • korzystanie ze sprzętu koprocesorowego, takiego jak karty graficzne obsługujące CUDA.
  • wykrywanie wirtualizacji i zmiana zachowania (być może próba uniknięcia zapisów w systemie plików)

Co do robienia tego czeku masz kilka opcji, najbardziej przydatną dla Intela jest instrukcja cpuid.

Alternatywnie ponownie zaimplementuj / zaktualizuj istniejący przy użyciu dostępnej dokumentacji na temat funkcji, których potrzebujesz.

Całkiem sporo osobnych dokumentów, aby dowiedzieć się, jak wykrywać rzeczy:

Duża część tego, za co zapłaciłbyś w bibliotece CPU-Z, to ktoś robi to wszystko (i paskudne małe problemy zaangażowany) dla Ciebie.


  1. uważaj z tym - trudno jest pokonać przyzwoite Kompilatory optymalizujące na tym
 5
Author: ShuggyCoUk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-24 18:44:18

Tak, to możliwe. Skompiluj wszystkie różnie zoptymalizowane wersje jako różne dynamiczne biblioteki ze wspólnym punktem wejścia i zapewnij plik wykonywalny, który ładuje się i uruchamia poprawną bibliotekę w czasie uruchamiania, poprzez punkt wejścia, w zależności od pliku konfiguracyjnego lub innych informacji.

 16
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-18 20:31:26

Czy Można używać skryptu?

Możesz wykryć PROCESOR za pomocą skryptu i dynamicznie załadować plik wykonywalny, który jest najbardziej zoptymalizowany pod kątem architektury. Może również wybrać wersje 32/64 bitowe.

Jeśli używasz Linuksa, możesz odpytywać procesor za pomocą

cat /proc/cpuinfo

Prawdopodobnie możesz to zrobić za pomocą skryptu bash / perl / python lub hosta skryptów windows w systemie windows. Prawdopodobnie nie chcesz zmuszać użytkownika do zainstalowania silnika skryptów. Taki, który działa na OS po wyjęciu z pudełka IMHO byłby najlepszy.

W rzeczywistości, w systemie windows prawdopodobnie chciałbyś napisać małą aplikację C#, aby łatwiej było odpytywać architekturę. Aplikacja C# może po prostu wywołać dowolny plik wykonywalny.

Alternatywnie możesz umieścić różne wersje kodu w bibliotekach dll lub obiektach współdzielonych, a następnie dynamicznie załadować je w oparciu o wykrytą architekturę. Tak długo, jak mają ten sam podpis połączenia powinno działać.

 6
Author: Byron Whitlock,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-18 20:23:27

Spójrz na liboil: http://liboil.freedesktop.org/wiki /. Może dynamicznie wybierać implementacje obliczeń związanych z multimediami w czasie wykonywania. Może się okazać, że możesz liboil się, a nie tylko jego techniki.

 5
Author: camh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-19 09:12:07

Skoro wspomniałeś, że używasz GCC, zakładam, że Twój kod jest w C (lub c++).

Neil Butterworth zasugerował już utworzenie osobnych bibliotek dynamicznych, ale wymaga to pewnych nietrywialnych rozważań międzyplatformowych (ręczne ładowanie bibliotek dynamicznych jest INNE na Linuksie, Windows, OSX itp., a poprawienie tego prawdopodobnie zajmie trochę czasu).

Tanim rozwiązaniem jest po prostu zapisanie wszystkich wariantów za pomocą unikalnych nazw i użycie wskaźnika funkcji, aby wybrać właściwy w czasie wykonywania.

Podejrzewam, że dodatkowa dereferencja spowodowana wskaźnikiem funkcji będzie amortyzowana przez rzeczywistą pracę, którą wykonujesz (ale będziesz chciał to potwierdzić).

Ponadto uzyskanie różnych optymalizacji kompilatora będzie prawdopodobnie wymagało innych .c/.pliki cpp, a także niektóre przekręcanie narzędzia do budowania. Ale to chyba mniej ogólna praca niż oddzielne biblioteki(które potrzebowały tego już w takiej czy innej formie).

 3
Author: jhoule,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-18 20:52:44

Ponieważ nie określiłeś, czy masz limity na liczbę plików, proponuję inne rozwiązanie: skompilować 5 plików wykonywalnych, a następnie utworzyć szósty plik wykonywalny, który uruchomi odpowiedni plik binarny. Oto jakiś pseudokod, dla Linuksa

int main(int argc, char* argv[])
{
    char* target_path[MAXPATH];
    char* new_argv[];
    char* specific_version = determine_name_of_specific_version();
    strcpy(target_path, "/usr/lib/myapp/versions");
    strcat(target_path, specific_version);

    /* append NULL to argv */
    new_argv = malloc(sizeof(char*)*(argc+1));
    memcpy(new_argv, argv, argc*sizeof(char*));
    new_argv[argc] = 0;
    /* optionally set new_argv[0] to target_path */

    execv(target_path, new_argv);
}

Na plus, takie podejście pozwala na zapewnienie użytkownikowi zarówno 32-bitowych, jak i 64-bitowych binariów, w przeciwieństwie do wszelkich metod bibliotecznych, które zostały zaproponowane. Na minus nie ma execv w Win32 (ale dobra emulacja w cygwin); w systemie Windows musisz utworzyć nowy proces, a nie ponownie wykonać bieżący.

 3
Author: Martin v. Löwis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-21 12:33:14

Wspomniałeś o kompilatorze Intela. To zabawne, ponieważ domyślnie może zrobić coś takiego. Jest jednak pewien haczyk. Kompilator Intela nie wstawił sprawdzeń pod kątem funkcjonalności SSE. Zamiast tego sprawdzili, czy masz konkretny chip Intela. Nadal byłby powolny przypadek domyślny. W rezultacie procesory AMD nie otrzymałyby odpowiednich wersji zoptymalizowanych pod kątem SSE. Wokół krążą hacki, które zastąpią sprawdzenie Intela odpowiednim sprawdzeniem SSE.

32/64 bity różnica będzie wymagała dwóch plików wykonywalnych. Zarówno format ELF, jak i PE przechowują te informacje w nagłówku plików wykonywalnych. Nie jest zbyt trudne, aby uruchomić wersję 32 bitów domyślnie, sprawdź, czy jesteś na systemie 64 bitowym, a następnie uruchom ponownie wersję 64 bitową. Ale może być łatwiejsze utworzenie odpowiedniego dowiązania symbolicznego w czasie instalacji.

 1
Author: MSalters,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-21 13:55:45

Pozwala podzielić problem na dwie części składowe. 1) Tworzenie zoptymalizowanego kodu zależnego od platformy i 2) budowanie na wielu platformach.

Pierwszy problem jest dość prosty. Enkapsuluje kod zależny od platformy w zestaw funkcji. Stwórz inną implementację każdej funkcji dla każdej platformy. Umieść każdą implementację w swoim własnym pliku lub zestawie plików. Jest to najłatwiejsze dla systemu budowania, jeśli umieścisz Kod każdej platformy w osobnym katalogu.

W części drugiej proponuję przyjrzeć się Gnu Atuotools(Automake, AutoConf i Libtool). Jeśli kiedykolwiek ściągnąłeś i zbudowałeś program GNU z kodu źródłowego, wiesz, że musisz go uruchomić ./ configure przed uruchomieniem make. Celem skryptu configure jest 1) sprawdzenie, czy Twój system posiada wszystkie wymagane biblioteki i narzędzia potrzebne do zbudowania i uruchomienia programu oraz 2) dostosowanie plików Makefile dla docelowej platformy. Autotools to zestaw narzędzi do generowania konfiguracji scenariusz.

Używając autoconf, możesz tworzyć małe makra, aby sprawdzić, czy maszyna obsługuje wszystkie instrukcje procesora, których potrzebuje twój kod zależny od platformy. W większości przypadków makra już istnieją, wystarczy skopiować je do skryptu autoconf. Następnie automake i autoconf mogą skonfigurować pliki Makefile tak, aby pobierały odpowiednią implementację.

To wszystko jest trochę za dużo dla tworzenia przykładu tutaj. Potrzeba trochę czasu, żeby się nauczyć. Ale cała dokumentacja się wyczerpała. tam. Istnieje nawet darmowa książka dostępna online. Proces ten ma zastosowanie do Twoich przyszłych projektów. W przypadku wsparcia dla wielu platform jest to naprawdę najbardziej niezawodny i najłatwiejszy sposób, jak sądzę. Wiele sugestii zamieszczonych w innych odpowiedziach to rzeczy, z którymi zajmuje się Autotools (wykrywanie procesora, obsługa statycznych i współdzielonych bibliotek) bez konieczności zbytniego myślenia o tym. Jedyną zmarszczką, z którą możesz mieć do czynienia, jest sprawdzenie, czy Autotools są dostępne dla MinGW. Wiem. są częścią Cygwin, jeśli można iść tą drogą zamiast.

 1
Author: Steve K,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-08-25 22:45:22