Zbuduj bombę kompilatora


372

Wprowadzenie

Jesteś prawdopodobnie zna bomby zip , bomb XML itp Mówiąc prościej, są (względnie) to małe pliki, które produkują ogromne wyjście kiedy interpretowane przez naiwnego oprogramowania. Wyzwaniem jest nadużycie kompilatora w ten sam sposób.

Wyzwanie

Napisz kod źródłowy, który zajmuje 512 bajtów lub mniej i który kompiluje się w plik, który zajmuje najwięcej możliwej przestrzeni. Największy plik wyjściowy wygrywa!

Zasady

OK, więc jest kilka ważnych wyjaśnień, definicji i ograniczeń;

  • Dane wyjściowe kompilacji muszą być plikiem ELF , przenośnym plikiem wykonywalnym systemu Windows (.exe) lub wirtualnym kodem bajtowym dla JVM lub CLR .Net (inne typy wirtualnego kodu bajtowego również prawdopodobnie będą OK, jeśli zostaną o to poproszone). Aktualizacja: Dane wyjściowe .pyc / .pyo Pythona również się liczą .
  • Jeśli wybranego języka nie można skompilować bezpośrednio w jednym z tych formatów, dozwolona jest również transpilacja, a następnie kompilacja ( Aktualizacja: możesz transpilować wiele razy, o ile nigdy nie używasz tego samego języka więcej niż jeden raz ).
  • Kod źródłowy może składać się z wielu plików, a nawet plików zasobów, ale suma wszystkich tych plików nie może przekraczać 512 bajtów.
  • Nie można używać innych danych wejściowych niż pliki źródłowe i standardowa biblioteka wybranego języka. Łączenie statyczne bibliotek standardowych jest OK, jeśli jest obsługiwane. W szczególności nie ma bibliotek stron trzecich ani bibliotek systemu operacyjnego.
  • Musi istnieć możliwość wywołania kompilacji za pomocą polecenia lub szeregu poleceń. Jeśli potrzebujesz specyficznych flag podczas kompilacji, liczą się one do limitu bajtów (np. Jeśli twoja linia kompilacji jest gcc bomb.c -o bomb -O3 -lm, -O3 -lmczęść (7 bajtów) zostanie policzona (zwróć uwagę, że początkowa spacja nie jest liczona).
  • Preprocesory są dozwolone tylko wtedy, gdy są standardową opcją kompilacji dla twojego języka.
  • Środowisko zależy od ciebie, ale aby uczynić to weryfikowalnym, trzymaj się najnowszych (tj. Dostępnych) wersji kompilatora i systemów operacyjnych (i oczywiście określ, którego używasz).
  • Musi się kompilować bez błędów (ostrzeżenia są OK), a awaria kompilatora nie ma znaczenia.
  • To, co faktycznie robi twój program , jest nieistotne, chociaż nie może być niczym złośliwym. Nie musi nawet być w stanie się uruchomić.

Przykład 1

Program C.

main(){return 1;}

Kompilowany z Apple LLVM version 7.0.2 (clang-700.1.81)OS X 10.11 (64-bit):

clang bomb.c -o bomb -pg

Tworzy plik 9228 bajtów. Całkowity rozmiar źródła to 17 + 3 (dla -pg) = 20 bajtów, co łatwo mieści się w limicie rozmiaru.

Przykład 2

Program Brainfuck:

++++++[->++++++++++++<]>.----[--<+++>]<-.+++++++..+++.[--->+<]>-----.--
-[-<+++>]<.---[--->++++<]>-.+++.------.--------.-[---<+>]<.[--->+<]>-.

Przeniesione z awib do c z:

./awib < bomb.bf > bomb.c

Następnie skompilowany z Apple LLVM version 7.0.2 (clang-700.1.81)OS X 10.11 (64-bit):

clang bomb.c

Tworzy plik 8464 bajtów. Łączna ilość danych wejściowych wynosi 143 bajty (ponieważ @lang_cjest domyślną opcją awib, nie trzeba jej dodawać do pliku źródłowego, a żadne polecenie nie ma specjalnych flag).

Należy również pamiętać, że w tym przypadku plik tymczasowy bomb.c ma 802 bajtów, ale nie ma to wpływu na rozmiar źródła ani rozmiar wyjściowy.

Ostatnia uwaga

Jeśli zostanie uzyskana moc wyjściowa większa niż 4 GB (być może jeśli ktoś znajdzie kompletny preprocesor Turinga), konkurencja będzie dotyczyć najmniejszego źródła, które tworzy plik o co najmniej takiej wielkości (po prostu nie jest praktyczne testowanie zgłoszeń, które stają się zbyt duże) .


Jeśli używasz transpilatora, czy wyjściowy kod źródłowy musi mieć mniej niż 512 bajtów, a także wejściowy kod źródłowy?
trichoplax

3
Czy dozwolona jest wielokrotna transpilacja?
orlp

3
@ LegionMammal978 tak, musi wytworzyć jeden z określonych przeze mnie typów plików. Ale jeśli uważasz, że znalazłeś coś, co jest bardziej maszyną wirtualną niż językiem interpretowanym, zapytaj o to konkretnie i możliwe, że pozwolę na to (jest to trochę subiektywne, więc chciałem być bardzo restrykcyjny, z opcją otwarcia)
Dave

3
@trichoplax Nie byłam tego świadoma, ale z niektórych lektur wygląda to tak; kompilacja do kodu bajtowego Pythona absolutnie się liczy. W przypadku Pythona rozmiar wyjściowy byłby sumarycznym rozmiarem wszystkich plików pyc / pyo. Niedługo zaktualizuję pytanie za pomocą tych aktualizacji opartych na komentarzach.
Dave

2
@MartinRosenau - WGroleau zadał już podobne pytanie; jest standardem w kodowaniu wyzwań, że możesz użyć wszystkiego, co istniało już w momencie rozpoczęcia wyzwania.
Dave

Odpowiedzi:


441

C, (14 + 15) = źródło 29 bajtów, plik wykonywalny 17 179 875 837 (16 GB)

Dzięki @viraptor za 6 bajtów off.

Dzięki @hvd za 2 bajty wyłączone i rozmiar pliku wykonywalnego x4.

Definiuje to mainfunkcję jako dużą tablicę i inicjuje jej pierwszy element. To powoduje, że GCC przechowuje całą tablicę w wynikowym pliku wykonywalnym.

Ponieważ ta tablica jest większa niż 2 GB, musimy dostarczyć -mcmodel=mediumflagę do GCC. Dodatkowe 15 bajtów jest uwzględnionych w wyniku, zgodnie z zasadami.

main[-1u]={1};

Nie oczekuj, że ten kod zrobi coś miłego po uruchomieniu.

Połącz z:

gcc -mcmodel=medium cbomb.c -o cbomb

Zajęło mi trochę czasu, aby przejść do testowania sugestii @ hvd - i znaleźć maszynę z wystarczającą ilością soku, aby sobie z tym poradzić. W końcu znalazłem starą nieprodukcyjną maszynę RedHat 5.6 z 10 GB pamięci RAM, 12 GB wymiany i / tmp ustawioną na dużą partycję lokalną. Wersja GCC to 4.1.2. Całkowity czas kompilacji około 27 minut.

Ze względu na obciążenie procesora i pamięci RAM odradzam wykonywanie tej kompilacji na dowolnym komputerze związanym z produkcją .



13
Gram przeciwko mojemu rozwiązaniu, ale ... nie potrzebujesz a. Możesz po prostu użyćmain[1<<30]={1};
viraptor

38
O mój. To jest złe. X zamarł na kilka minut, próbując skompilować ten kod. Zaczynałem szukać innego komputera, aby ewentualnie ssh wrócił i zabił proces gcc, zanim w końcu wrócił do życia. Btw. Jeśli chcesz mieć większą wartość niż 1<<30wtedy, 7<<28może być opcją.
kasperd

33
> 4 GB? To szybko się zwiększyło
Wayne Werner,

18
Na wypadek, gdyby ktoś zastanawiał się, dlaczego to się kompiluje: stackoverflow.com/questions/34764796/...
TC

206

C #, około 1 min do skompilowania, wyjście binarne 28 MB:

class X<A,B,C,D,E>{class Y:X<Y,Y,Y,Y,Y>{Y.Y.Y.Y.Y.Y.Y.Y.Y y;}}

Dodanie większej liczby Y spowoduje wykładniczy wzrost rozmiaru.

Wyjaśnienie Pharap zgodnie z prośbą @Odomontois:

Ta odpowiedź nadużywa parametrów dziedziczenia i typu do tworzenia rekurencji. Aby zrozumieć, co się dzieje, łatwiej jest najpierw uprościć problem. Zastanów się class X<A> { class Y : X<Y> { Y y; } }, który generuje klasę ogólną X<A>, która ma klasę wewnętrzną Y. X<A>.Ydziedziczy X<Y>, stąd X<A>.Yteż ma klasę wewnętrzną Y, która jest wtedy X<A>.Y.Y. Ma to również klasę wewnętrzną Y, a ta klasa wewnętrzna Yma klasę wewnętrzną Yitp. Oznacza to, że możesz używać scope resolution ( .) ad infinitum i za każdym razem, gdy go używasz, kompilator musi wydedukować inny poziom dziedziczenia i parametryzację typu .

Dodając dodatkowe parametry typu, praca kompilatora na każdym etapie jest zwiększana.

Rozważ następujące przypadki:
W class X<A> { class Y : X<Y> { Y y;} }typie parametr Ama typ X<A>.Y.
W class X<A> { class Y : X<Y> { Y.Y y;} }typ param Ama typ X<X<A>.Y>.Y.
W class X<A> { class Y : X<Y> { Y.Y.Y y;} }typ param Ama typ X<X<X<A>.Y>.Y>.Y.
W class X<A,B> { class Y : X<Y,Y> { Y y;} }typie param Ajest X<A,B>.Yi Bjest X<A,B>.Y.
W class X<A> { class Y : X<Y> { Y.Y y;} }typie param Ajest X<X<A,B>.Y, X<A,B>.Y>.Yi Bjest X<X<A,B>.Y, X<A,B>.Y>.Y.
W class X<A> { class Y : X<Y> { Y.Y.Y y;} }typie param Ajest X<X<X<A,B>.Y, X<A,B>.Y>.Y, X<X<A,B>.Y, X<A,B>.Y>.Y>.Yi Bjest X<X<X<A,B>.Y, X<A,B>.Y>.Y, X<X<A,B>.Y, X<A,B>.Y>.Y>.Y.

W następstwie tego wzorca, można sobie wyobrazić tylko 1 pracy kompilator musiałby zrobić, by wydedukować, co Asię EY.Y.Y.Y.Y.Y.Y.Y.Yw definicji class X<A,B,C,D,E>{class Y:X<Y,Y,Y,Y,Y>{Y.Y.Y.Y.Y.Y.Y.Y.Y y;}}.

1 Możesz to rozgryźć, ale potrzebujesz dużo cierpliwości, a inteligencja ci tutaj nie pomoże.


14
To bardziej przypomina szaleństwo, którego się spodziewałem! Wygląda na to, że mam zamiar ponownie zainstalować Mono…
Dave

31
Czy możesz wyjaśnić tak notoryczny efekt?
Odomontois,

16
+1 za zrobienie czegoś więcej niż tylko zainicjowanie dużej tablicy.
Stig Hemmer

6
Oto przykład użycia Try Roslyn i zaledwie 3 Ys .
Kobi

10
Widziałem to pytanie i od razu pomyślałem o tobie. Miły!
Eric Lippert,

154

Python 3, 13-bajtowe źródło, 9 057,900,463 bajtów (8,5GiB) .pyc-file

(1<<19**8,)*2

Edycja : Zmieniłem kod na powyższą wersję po tym, jak zdałem sobie sprawę, że reguły mówią, że wielkość wyjściowa powyżej 4GiB nie ma znaczenia, a kod dla tego jest jeszcze trochę krótszy; Poprzedni kod - i co ważniejsze wyjaśnienie - można znaleźć poniżej.


Python 3, 16 bajtowe źródło, plik .pyc> 32 TB (jeśli masz wystarczającą ilość pamięci, miejsca na dysku i cierpliwości)

(1<<19**8,)*4**7

Objaśnienie: Python 3 ciągle się składa, a duże wykładziny szybko się potęgują. Format używany w plikach .pyc przechowuje długość reprezentacji liczb całkowitych przy użyciu 4 bajtów, a w rzeczywistości limit wydaje się bardziej podobny 2**31, więc używając tylko potęgowania do wygenerowania jednej dużej liczby, limit generuje 2 GB. plik pyc ze źródła 8-bajtowego. ( 19**8jest nieco nieśmiały 8*2**31, więc 1<<19**8ma reprezentację binarną nieco poniżej 2 GB; mnożenie przez osiem to dlatego, że chcemy bajtów, a nie bitów)

Jednak krotki są również niezmienne, a mnożenie krotki jest również stale składane, więc możemy powielić ten blob 2 GB tyle razy, ile chcemy, przynajmniej 2**31prawdopodobnie. 4**7Dostać się do 32TB został wybrany tylko dlatego, że był to pierwszy wykładnik udało mi się znaleźć, że piłka poprzednią 16TB odpowiedź.

Niestety, mając pamięć na swoim komputerze, mogłem to przetestować tylko do mnożnika 2, tj. (1<<19**8,)*2, który wygenerował plik 8,5 GB, co mam nadzieję pokazuje, że odpowiedź jest realistyczna (tzn. rozmiar pliku nie jest ograniczony do 2 ** 32 = 4 GB).

Poza tym nie mam pojęcia, dlaczego rozmiar pliku, który uzyskałem podczas testowania, wynosił 8,5 GB zamiast 4 GB, czego się spodziewałem, a plik jest na tyle duży, że w tej chwili nie mam ochoty się w niego grzebać.


2
+1, ale dlaczego nie (1<<19**8,)*2? 4 GB wystarczy.
Akangka

2
@ChristianIrwan: Tak, zapomniałem o tej zasadzie, zdałem sobie sprawę z niej kilka minut temu i nie zastanawiałem się, jakiego rodzaju edycji powinienem dokonać. :-)
Aleksi Torhamo

1
Miły. Ponieważ jest to tylko 13 bajtów, w końcu mamy wyzwanie dla pierwszej odpowiedzi! Mogłem tylko potwierdzić 1<<18na moim komputerze (1,5 GB), ale przetestuję go później na Linuksie, gdzie spodziewam się, że będzie działał z pełnymi 8 GB (nie zamierzam wypróbować wersji 32 TB!)
Dave,

1
@Dave: Dokładny rozmiar może zależeć od wersji (1,5 GB brzmi dziwnie bez względu na wszystko); Korzystałem z Python 3.3.5 i użyłem python -m py_compile asd.pydo wygenerowania pliku .pyc.
Aleksi Torhamo

3
IIRC, python używa 30 bitów na 32-bitowe słowo w swojej reprezentacji liczb całkowitych

130

Jeśli zostanie uzyskana moc wyjściowa większa niż 4 GB (być może jeśli ktoś znajdzie kompletny preprocesor Turinga), konkurencja będzie dotyczyć najmniejszego źródła, które tworzy plik o co najmniej takiej wielkości (po prostu nie jest praktyczne testowanie zgłoszeń, które stają się zbyt duże) .

„Szablon Haskell” pozwala na generowanie kodu Haskell w czasie kompilacji przy użyciu Haskell, a zatem jest kompletnym wstępnym procesorem.

Oto moja próba, sparametryzowana dowolnym wyrażeniem liczbowym FOO:

import Language.Haskell.TH;main=print $(ListE .replicate FOO<$>[|0|])

Magia to kod wewnątrz „splice” $(...). Zostanie to wykonane w czasie kompilacji, aby wygenerować Haskell AST, który zostanie wszczepiony w AST programu zamiast połączenia.

W tym przypadku tworzymy prosty AST reprezentujący literał 0, replikujemy te FOOczasy, aby utworzyć listę, a następnie używamy ListEz Language.Haskell.THmodułu, aby przekształcić tę listę AST w jeden duży AST reprezentujący literał [0, 0, 0, 0, 0, ...].

Powstały program jest równoważny main = print [0, 0, 0, ...]z FOOpowtórzeniami 0.

Aby skompilować do ELF:

$ ghc -XTemplateHaskell big.hs
[1 of 1] Compiling Main             ( big.hs, big.o )
Linking big ...
$ file big
big: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /nix/store/mibabdfiaznqaxqiy4bqhj3m9gaj45km-glibc-2.21/lib/ld-linux.so.2, for GNU/Linux 2.6.32, not stripped

Waży to 83 bajty (66 dla kodu Haskell i 17 dla -XTemplateHaskellargumentu) plus długość FOO.

Możemy uniknąć argumentu kompilatora i po prostu skompilować ghc, ale musimy umieścić {-# LANGUAGE TemplateHaskell#-}na początku, co powoduje wzrost kodu do 97 bajtów.

Oto kilka przykładowych wyrażeń FOOi wielkość wynikowego pliku binarnego:

FOO         FOO size    Total size    Binary size
-------------------------------------------------
(2^10)      6B          89B           1.1MB
(2^15)      6B          89B           3.6MB
(2^17)      6B          89B           12MB
(2^18)      6B          89B           23MB
(2^19)      6B          89B           44MB

Skończyło mi się kompilowanie pamięci RAM (2^20).

Możemy również stworzyć nieskończoną listę, używając repeatzamiast replicate FOO, ale to zapobiega kompilatorowi;)


46
Witamy w Programowaniu zagadek i Code Golf. To świetna odpowiedź, szczególnie dla nowego użytkownika tej witryny. Jeśli potrzebujesz pomocy (w co wątpię), nie krępuj się zapytać.
wizzwizz4,

3
@ wizzwizz4: Tak, to świetna odpowiedź. Jest zasadniczo taki sam jak mój, z tym wyjątkiem, że w Haskell wymaga specjalnej dyrektywy kompilatora, aby metaprogramowanie działało. ;)
Mason Wheeler

2
Podczas kompilacji z GHC 7.8.3 pojawia się komunikat „Poza zakresem: <<>>” (ustawiam kod na [...].replicate (2^10)<$>[|0|])). Nie mam doświadczenia z Haskellem; jakieś wskazówki, jak to skompilować?
Dave

38
Szkoda, że ​​szablon haskell nie jest wystarczająco leniwy, aby przesyłać strumieniowo nieskończony plik wykonywalny.
PyRulez

1
Cześć @Dave <$>funkcja jest szeroko stosowana w Haskell, ale została przeniesiona tylko do „preludium” (zestawu funkcji dostępnych domyślnie) w GHC 7.10. W przypadku wcześniejszych wersji należy dodać import Control.Applicative;po istniejącym importoświadczeniu. Właśnie próbowałem z GHC 7.8.4 i działa.
Warbo

80

C ++, 250 + 26 = 276 bajtów

template<int A,int B>struct a{static const int n;};
template<int A,int B>const int a<A,B>::n=a<A-1,a<A,B-1>::n>::n;
template<int A>struct a<A,0>{static const int n=a<A-1,1>::n;};
template<int B>struct a<0,B>{static const int n=B+1;};
int h=a<4,2>::n;

Jest to funkcja Ackermann zaimplementowana w szablonach. Nie mogę się skompilować h=a<4,2>::n;na moim małym (6 GB) komputerze, ale udało mi się h=a<3,14>uzyskać plik wyjściowy o wielkości 26 MB. Możesz dostroić stałe, aby przekroczyć granice platformy - wskazówki znajdziesz w linkowanym artykule z Wikipedii.

Wymaga -gflagi dla GCC (ponieważ wszystkie symbole debugowania faktycznie zajmują dowolne miejsce) i głębia szablonu większa niż domyślna. Moja linia kompilacji zakończyła się jako

g++ -ftemplate-depth=999999 -g -c -o 69189.o 69189.cpp

Informacje o platformie

g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2
Linux 3.13.0-46-generic #79-Ubuntu SMP x86_64 GNU/Linux

Naprawdę podoba mi się ten, ale nie jestem pewien, czy mogę zaakceptować wyjście .o, ponieważ powiedziałem ELF / .exe / itp. (i skompilowanie tego w pełni optymalizuje to wszystko!). Mimo to +1 (i potwierdzono)
Dave

4
Aktualizacja: Jak Ben Voigt zwraca uwagę na jego odpowiedź, GCC w systemie Linux ma generować pliki ELF jako .o wyjścia, a ja byłem w stanie potwierdzić wariant <3,14> z nim, tak yup - to jest ważne.
Dave

17
Spodziewałem się, że z szablonów C ++ wyjdzie coś absurdalnego. I nie spodziewa funkcję Ackermann.
Mark

czy Fibonacci nie da ci mniejszego kodu i lepszej kontroli rozmiaru wyjściowego?
Czy Ness

1
Ale chcemy większego kodu! Fibonacci daje prawie taki sam rozmiar jak czysty kod liniowy (ale dłuższy czas kompilacji niż liniowy). Z pewnością możesz się dobrze bawić przy statycznej tablicy rozmiarów A+Bw każdej klasie, teraz myślę o tym ...
Toby Speight

65

ASM, 61 bajtów (źródło 29 bajtów, 32 bajty na flagi), plik wykonywalny 4,294,975,320 bajtów

.globl main
main:
.zero 1<<32

Połącz z gcc the_file.s -mcmodel=large -Wl,-fuse-ld=gold


5
1<<30jest wystarczający dla C. Ponieważ jest to asembler, rozmiar jest w bajtach.
viraptor

2
@viraptor Mój system ma 32 GB pamięci RAM i dla kopnięć próbowałem zbudować kod. asudaje się oddać off ld, ale ldnie z tego . Nawet nie -mcmodel=mediumwydaje się pomóc.
Nie będę istniał Idonotexist

2
spróbuj wymusić użycie goldlinkera: gcc -fuse-ld=gold ...kompiluje / łączy ... eek! Zakończone w 1:29 (89 sekund) i rozmiarze 1 073 748 000 bajtów.
lornix

2
W końcu udało mi się to skompletować na 64-bitowym Ubuntu 15.10 z wywołaniem gcc -o g g.s -mcmodel=large -Wl,-fuse-ld=gold. Końcowe podsumowanie:, 4,294,975,320 bytesz 32 dodatkowymi bajtami dodanymi do długości programu dla -mcmodel=large -Wl,-fuse-ld=gold. Warto zauważyć, że nagłówek jest niepoprawny; źródło ma 29 bajtów (bez dodanych dodatkowych flag).
Mego

3
Zwiększając przydział do 1<<33, 8,589,942,616otrzymałem bajtowy plik wykonywalny.
Mego

60

Oto moja odpowiedź C z 2005 roku. Wytworzyłby plik binarny 16 TB, gdybyś miał 16 TB pamięci RAM (nie masz).

struct indblock{
   uint32_t blocks[4096];
};

struct dindblock {
    struct indblock blocks[4096];
};

struct tindblock {
    struct dindblock blocks[4096];
};

struct inode {
    char data[52]; /* not bothering to retype the details */
    struct indblock ind;
    struct dindblock dint;
    struct tindblock tind;
};

struct inode bbtinode;

int main(){}

19
„Wytworzyłby plik binarny 16 TB, gdybyś miał 16 TB pamięci RAM (nie masz).” - ja też nie mam dysku twardego 16 TB! Naprawdę nie mogę tego zweryfikować, ale mimo to jest fajne.
Dave

5
Odkryłem go przypadkiem i obserwowałem, jak kompilator się przewraca, gdy zabrakło miejsca na adres.
Jozuego

8
NIE próbuj golfować tego wpisu; golfa pokonuje cel próbki kodu i i tak nie ma w tym żadnych korzyści. Kod jest już na licencji GPL od 2005 roku.
Joshua

6
@BenVoigt Bez względu na to, edytowanie kodu innych osób jest tutaj nigdy nie do przyjęcia. Zostaw komentarz, jeśli występuje problem. Odpowiedni meta post: meta.codegolf.stackexchange.com/questions/1615/…
Mego

2
@Joshua: Sprawdź różnicę w przecenach. Mego dodał tylko wskazówkę dotyczącą wyróżnienia.
n̴̖̋h̷͉̃a̷̭̿h̸̡̅ẗ̵̨d̷̰̀ĥ̷̳

25

Zwykły stary preprocesor C: wejście 214 bajtów, wyjście 5 MB

Zainspirowany moim prawdziwym preprocesorem zawiodłem tutaj .

#define A B+B+B+B+B+B+B+B+B+B
#define B C+C+C+C+C+C+C+C+C+C
#define C D+D+D+D+D+D+D+D+D+D
#define D E+E+E+E+E+E+E+E+E+E
#define E F+F+F+F+F+F+F+F+F+F
#define F x+x+x+x+x+x+x+x+x+x

int main(void) { int x, y = A; }

Eksperymenty pokazują, że każdy poziom #defines spowoduje (zgodnie z oczekiwaniami) wydajność około dziesięciokrotnie większą. Ale ponieważ ten przykład skompilował się dłużej niż godzinę, nigdy nie przeszedłem do „G”.


9
To trochę jak bomba xml
skorek

9
W szczególności jest to implementacja oryginalnego „Billion Laughs”.
mınxomaτ

To szalone, ale proste.
Vahid Amiri

2
Wow, to faktycznie powoduje awarie w GCC 4.9 i Clang. Z jakiego kompilatora korzystałeś?
Dave

1
@Dave: Dziwne. Kiedy kompiluję za pomocą make, kompiluje się, ale jeśli wpisuję dokładnie to samo polecenie, które make używa, ulega awarii. I wydaje się, że nie ma to związku ze zmiennymi środowiskowymi.
Thomas Padron-McCarthy

24

Java, źródło 450 + 22 = 472 bajtów, plik klasy ~ 1GB

B.java (wersja golfowa, ostrzeżenie podczas kompilacji)

import javax.annotation.processing.*;@SupportedAnnotationTypes("java.lang.Override")public class B extends AbstractProcessor{@Override public boolean process(java.util.Set a,RoundEnvironment r){if(a.size()>0){try(java.io.Writer w=processingEnv.getFiler().createSourceFile("C").openWriter()){w.write("class C{int ");for(int i=0;i<16380;++i){for(int j=0;j<65500;++j){w.write("i");}w.write(i+";int ");}w.write("i;}");}catch(Exception e){}}return true;}}

B.java (wersja bez golfa)

import java.io.Writer;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;

@SupportedAnnotationTypes("java.lang.Override")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class B extends AbstractProcessor {
    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (annotations.size() > 0) {
            try (Writer writer = processingEnv.getFiler().createSourceFile("C").openWriter()) {
                writer.write("class C{int ");
                for (int i = 0; i < 16380; ++i) {
                    for (int j = 0; j < 65500; ++j) {
                        writer.write("i");
                    }
                    writer.write(i + ";int ");
                }
                writer.write("i;}");
            } catch (Exception e) {
            }
        }
        return true;
    }
}

Kompilacja

javac B.java
javac -J-Xmx16G -processor B B.java

Wyjaśnienie

Ta bomba używa procesorów adnotacji. Wymaga 2 przejść kompilacyjnych. Pierwszy przebieg buduje klasę procesora B. Podczas drugiego przejścia procesor tworzy nowy plik źródłowy C.javai kompiluje go do plikuC.class rozmiaru1,073,141,162 bajtów.

Istnieje kilka ograniczeń podczas próby utworzenia dużego pliku klasy:

  • Utworzenie identyfikatorów dłuższych niż około 64 tys. Powoduje: error: UTF8 representation for string "iiiiiiiiiiiiiiiiiiii..." is too long for the constant pool .
  • Utworzenie ponad około 64 000 zmiennych / funkcji powoduje: error: too many constants
  • Istnieje również limit około 64k dla rozmiaru kodu funkcji.
  • Wydaje się, że istnieje ogólny limit (błąd?) W kompilatorze Java wynoszący około 1 GB dla .classpliku. Jeśli zwiększę 16380do16390 powyższego kodu, kompilator nigdy nie zwraca.
  • .javaPlik ma również limit około 1 GB . Zwiększenie 16380do 16400powyższego kodu powoduje: An exception has occurred in the compiler (1.8.0_66). Please file a bug ...po którym następuje java.lang.IllegalArgumentException.

10
Schludny; zasadniczo stworzyłeś swój własny preprocesor, w ramach limitu rozmiaru, w języku z kompilatorem, który natywnie obsługuje niestandardowe preprocesory. Jest to zgodne z zasadami. Ostateczna klasa wyniosła dla mnie tylko 0,5 GB, ale mogę potwierdzić metodę.
Dave

Kolejny przykład w języku Java habrahabr.ru/post/245333 - wykorzystuje zagnieżdżony try..finally(kod w końcu bloku jest duplikowany dla normalnych i wyjątkowych przypadków) i blok inicjalizujący (kod z bloku inicjującego jest dołączany do każdego konstruktora)
Victor

I otrzymuje äprzez ii dostosowane numery. Teraz bomba powinna stworzyć klasę 1 GB w dowolnym systemie bez problemów z kodowaniem. Jednak teraz potrzebuje dużo więcej pamięci.
Sleafar

? rozszerza TypeElement?!?
kot


22

C, źródło 26 bajtów, wyjście 2139103367 bajtów, poprawny program

const main[255<<21]={195};

Opracowano przy użyciu: gcc cbomb.c -o cbomb (gcc wersja 4.6.3, Ubuntu 12.04, ~ 77 sekund)

Pomyślałem, że spróbuję zobaczyć, jak duży mogę zrobić prawidłowy program bez użycia opcji wiersza poleceń. Pomysł mam z tej odpowiedzi: https://codegolf.stackexchange.com/a/69193/44946 przez Digital Trauma. Zobacz komentarze tam, dlaczego to się kompiluje.

Jak to działa: constUsuwa flagę zapisu ze stron w segmencie, dzięki czemu main można wykonać. Jest 195to kod maszynowy Intel do zwrotu. A ponieważ architektura Intela ma charakter endian, jest to pierwszy bajt. Program zakończy działanie bez względu na kod startowy umieszczony w rejestrze eax, prawdopodobnie 0.

To tylko około 2 gig, ponieważ linker używa 32-bitowych podpisanych wartości dla przesunięć. Jest o 8 megapikseli mniejszy niż 2 gig, ponieważ kompilator / linker potrzebuje trochę miejsca do pracy i jest to największy, jaki udało mi się uzyskać bez błędów linkera - ymmv.


3
Ciekawostką jest fakt, że dane wyjściowe to 2 078 451 bajtów skompresowanych przy maksymalnej kompresji = 1029: 1.
Zakipu

20

Boo , 71 bajtów. Czas kompilacji: 9 minut. 134.222.236 bajtów wykonywalny

macro R(e as int):
 for i in range(2**e):yield R.Body
x = 0
R 25:++x

Używa makra R(dla Powtarzania), aby kompilator pomnożył instrukcję przyrostu dowolną liczbę razy. Nie są wymagane żadne specjalne flagi kompilatora; po prostu zapisz plik jako bomb.booi uruchom kompilator, booc bomb.booaby go skompilować.


2**e-co to jest? Spróbuj 9**e!
wchargin 13.01.16

1
@WChargin: Zabawne w metaprogramowaniu jest to, jak łatwo można go dostosować!
Mason Wheeler,

Mam trochę problemów z instalacją boo… Potwierdzę to, gdy uda mi się go zainstalować!
Dave

@Dave Jakie masz z tym kłopoty?
Mason Wheeler,

16

Kotlin , źródło 90 bajtów, 177416 bajtów (173 KB) skompilowany plik binarny JVM

inline fun a(x:(Int)->Any){x(0);x(1)}
fun b()=a{a{a{a{a{a{a{a{a{a{a{println(it)}}}}}}}}}}}

Technicznie możesz to wydłużyć jeszcze bardziej, zagnieżdżając wyrażenie. Jednak kompilator ulega awarii z StackOverflowbłędem, jeśli zwiększysz rekurencję.


Twoje prefiksy SI nie zgadzają się. Czy to 177416 kilobajtów = 173 MB, czy 177416 bajtów = 173 kB?
Ben Voigt

1
@BenVoigt Dziękujemy za zwrócenie na to uwagi: D
TheNumberOne

Imponujące, mają +1
J Atkin

Aby Kotlin 1.2.20 mógł się skompilować, musimy usunąć jedną głębokość, która wynosi ~ 104kB. Z której wersji korzystałeś pierwotnie?
TWiStErRob

15

C ++, 214 bajtów (nie są wymagane specjalne opcje kompilacji)

#define Z struct X
#define T template<int N
T,int M=N>Z;struct Y{static int f(){return 0;}};T>Z<N,0>:Y{};T>Z<0,N>:Y{};T,int M>Z{static int f(){static int x[99999]={X<N-1,M>::f()+X<N,M-1>::f()};}};int x=X<80>::f();

Jest to dość prosta dwuwymiarowa rekurencja szablonu (głębokość rekurencji jest równa pierwiastkowi kwadratowemu całkowitej liczby wysłanych szablonów, więc nie przekroczy limitów platformy), z małą ilością statycznych danych w każdym z nich.

Wygenerowany plik obiektowy g++ 4.9.3 x86_64-pc-cygwinma 2567355421 bajtów (2.4GiB).

Zwiększenie wartości początkowej powyżej 80 przerywa asembler gcc cygwin (zbyt wiele segmentów).

Ponadto 99999można go zastąpić 9<<19lub w podobny sposób, aby zwiększyć rozmiar bez zmiany kodu źródłowego ... ale nie sądzę, że potrzebuję więcej miejsca na dysku niż już jestem;)


Potwierdzono (w rzeczywistości jest to 2.56 GB z clang), ale potrzebuje -cflagi kompilacji, aby zatrzymać linker (2 dodatkowe bajty), i nie jestem pewien, czy mogę zaakceptować wyjście .o (nie jeden z tych, które wymieniłem). Wciąż mi się podoba, więc +1.
Dave

@Dave: Pliki gcc .o mają format ELF, prawda?
Ben Voigt,

Niepewny. Nie zaczynają się od magicznej liczby ELF, kiedy je generuję… Zbadam później.
Dave

@Dave: Cóż, cygwin gcc nie generuje pliku ELF. Linux gcc wydaje się (chociaż patrzę na jeden z innego fragmentu kodu)
Ben Voigt

Tak, GCC 5.2.1 na Kubuntu rzeczywiście generuje plik ELF, ale to tylko 9 MB! Nie jestem pewien, jak udało się go tak mocno skompresować w porównaniu do innych kompilatorów. Może GCC 4.9 stworzy plik ELF o wielkości 2 GB.
Dave

6

Scala - źródło 70 bajtów, wynik 22980842 bajtów (po słoiku)

import scala.{specialized => s}
class X[@s A, @s B, @s C, @s D, @s E]

Daje to 9 5 (około 59 000) wyspecjalizowanych plików klas, które są pakowane w słoik o wielkości około 23 MB. Zasadniczo możesz kontynuować, jeśli masz system plików, który może obsłużyć tyle plików i wystarczającą ilość pamięci.

(Jeśli polecenie jar musi być dołączone, ma 82 bajty).


Nie mogłem go skompilować: error: java.lang.OutOfMemoryError: GC overhead limit exceeded. Czy możesz również udokumentować wymagane polecenie do kompilacji?
P.Péter

@ P.Péter - Musisz dać kompilatorowi więcej pamięci, np. scalac -J-Xmx12G X.scalaTo, czego użyłem. Nie sprawdziłem, ile tak naprawdę potrzebuje.
Rex Kerr

wciąż nie kompiluje się niestety :( error: error while loading AnnotatedElement, class file '/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar(java/lang/reflect/AnnotatedElement.class)' is broken (bad constant pool tag 18 at byte 76) one error foundCzy możesz podać wersję scala i java (być może także platformę)? Użyłem skalaca 2.9.2 i OpenJDK 1.8.0_66-internal-b17 na Debianie 8 x86-64.
P.Péter

Ubuntu 15.10, java version "1.8.0_72-ea" Java(TM) SE Runtime Environment (build 1.8.0_72-ea-b05) Java HotSpot(TM) 64-Bit Server VM (build 25.72-b05, mixed mode) ,$ scala -version Scala code runner version 2.11.7 -- Copyright 2002-2013, LAMP/EPFL
Rex Kerr

2

C 284 + 2 bajty dla -cIN gcc bomb.c -o bomb.o -c; wyjście: 2 147 484 052 bajtów

#define a 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
#define b a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a
#define c b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b
#define d c,c,c,c,c,c,c,c,c,c,c,c,c,c,c,c
#define e d,d,d,d,d,d,d,d,d,d,d,d,d,d,d,d
#define f e,e,e,e,e,e,e,e,e,e,e,e,e,e,e,e
__int128 x[]={f,f,f,f,f,f,f,f};

0

Buczenie, o wiele więcej, niż można się po tym spodziewać

macro R(e as int):for i in range(9**e):yield R.Body
x = 0
R 99:++x

Wygląda to jak odpowiedź Masona Wheelera z kilkoma małymi zmianami (??). Czy osiągnąłeś tę samą odpowiedź niezależnie, czy jest coś ważnego w zmienionych wartościach (jeśli tak, edytuj odpowiedź, aby wyjaśnić, dlaczego są one ważne).
Dave

0

Python 3:

9**9**9**9**9

Bomba tetracyjna


2
Powinieneś wskazać, ile bajtów ma wynik, aby zobaczyć, jak Twój wpis wypada w porównaniu z innymi.
Sanchises

Witamy w PPCG! Wygląda na to, że przypadkowo utworzyłeś dwa konta i wysłałeś tę odpowiedź dwukrotnie. Usunąłem drugą odpowiedź. Jak powiedział Sanchises, wyzwanie to zależy od rozmiaru skompilowanego programu . Dlatego powinieneś dołączyć ten rozmiar do swojej odpowiedzi, ponieważ jest to wynik główny. Zauważ też, że rzeczywisty program nie będzie bardzo duży, tylko wyrażenie, które tworzysz w pamięci, więc możesz pomyśleć o innym podejściu.
Martin Ender

1
@MartinEnder ze względu na to, jak Python ocenia niektóre wyrażenia w czasie kompilacji i przechowuje liczby z dowolną dokładnością, teoretycznie będzie to miało dość duży plik wykonywalny. Ale jak zauważył Aleksi Torhamo (który użył tej samej techniki w części swojej odpowiedzi), ma to limit około 2 GB, więc spodziewam się, że ten kod w postaci, w której napisano, prawdopodobnie się nie skompiluje (chociaż nie sprawdziłem ). Jeśli OP może zmusić go do skompilowania i opublikuje skompilowany rozmiar (wraz z poleceniem potrzebnym do jego wygenerowania), oznacza to, że jest poprawny. Podobieństwo do istniejącej odpowiedzi Aleksiego wydaje mi się zbiegiem okoliczności.
Dave
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.