Kalkulator ONP - C++ vs Delphi

Kalkulator ONP - C++ vs Delphi
msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:5 miesięcy
1

Ok, zawziąłem się i przepisałem cały kod (teraz w sumie kod jest w C, ale formalnie dla celów konkursu stwierdzam że pisałem w C++).

Trochę mi to zajęło... Ale wynik przerósł moje oczekiwania 0_o. Jakieś 4 razy.

Czas wykonania dla 800000 elementów wynosi... 0.239620 sekundy. W wersji Debug.
Na Release wynosi 0.192682, w porywach do 0.17xxxx. Nieźle.

Dziękuję, do widzenia, C99 owns u.

ExtremeRpn.zip

PS. znajdźcie w tym kodzie jakiś błąd bo mi szkoda Delphi.

edit: reupload, znalazłem 'drobny' błąd ale dał się poprawić bez zmiany wydajności

edytowany 4x, ostatnio: msm
MA
cóż, ja nie będę kaleczyć przepisując Delphi na nieobiektowy kod :) W każdym razie gratz :]
OO
A ile miało delphi na tym samym sprzęcie? Coś koło 1s?
msm
Mniej więcej tyle samo co u maciejmta - koło 1 s.
OO
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 13 lat
  • Postów:98
0

Łał, właśnie obejrzałem ten kod i jestem w szoku bo jest nawet ładny o_O
Spodziewałem się jakiegoś magicznego potwora a tu takie cudeńko.

Ale w dalszym ciągu Delphi nas szokuje bliskością wydajności do normalnie pisanego cepa. A zawsze to niby było takie wolne :)


O̾..͠o
msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:5 miesięcy
0

@O_o - dzięki, starałem się pisać ładny kod. Magicznych potworków tam jest parę tylko są ładnie popakowane w moduły :)

Na przykład:

Kopiuj
typedef union
{
	struct
	{
		char operator_type;
		char priority;
		unsigned short int item_type;
	};
	float number;
} rpnItem;

Nawet sam wygląd jest 'fajny' (jest to w sumie niezgodne ze standardem C w związku z czym wymaga w gcc kompilacji z parametrem -fms-extensions. Chodzi o anonimowe unie i struktury które są zresztą tylko kwestią estetyki. No i w C++ są w standardzie).
Ale o co chodzi - wszystkie itemy - czyli operatory i wartości są przechowywane w takiej unii (wielkości 4 bajtów każda). W pamięci wygląda to tak:

Kopiuj
/------------------------------------/
/ op_type |  prior  |   item_type    /
/              number                /
/------------------------------------/

Jeśli item_type == 0xFFFF to znaczy że mamy do czynienia z operatorem i możemy używać pól 'operator_type' i 'priority', w przeciwnym wypadku to liczba i korzystamy z pola number.
Na pierwszy rzut oka wydaje się to kiepskim i dość ryzykownym rozwiązaniem - co jeśli numer będzie akurat miał tak ustawione te dwa bajty? Hackiem tutaj jest właśnie zauważenie że nie będzie miał, bo float mający exponent ustawiony na 0xF i niezerową mantystę jest traktowany jako NaN user image

'Sztuczek' jest jeszczę parę, ale może się już nie będę pogrążał ;)

edytowany 3x, ostatnio: msm
0

@msm: niezły wynik, gratuluje!
A możesz zrobić taką wersję, która wyświetla wynik? Bo jak historia uczy to jest najlepszy sprawdzian poprawności programu.
Pod VS 2010 się nie kompiluje, nawet po zmianie typu języka na "C" - wywala się na przypisywaniu struktury:

1>......\c\src\main.c(10): error C2275: 'swHandle' : illegal use of this type as an expression
1> \stopwatch.h(12) : see declaration of 'swHandle'

OO
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 13 lat
  • Postów:98
0

Za dużo magii w tym jest by heretyczny VS to zjadł :)
A co się stanie z tym trikiem z exponentem na 0xF jeśli coś się zmieni na poziomie binarnym? Np odwrócenie końcówkowości, wtedy by trzeba było obrócić całą unie żeby było tak samo (chyba)


O̾..͠o
msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:5 miesięcy
0

@Cplus - VS jest z tego co pamiętam dość restrykcyjne jeśli chodzi o kod C, sprawdź czy przeniesienie deklaracji swHandle przed jakiekolwiek wyrażenie (na początek funkcji) pomoże.
W dodatku anonimowa struktura raczej nie zadziała... Jeśli chcesz mogę to przepisać tak żeby dało się skompilować w VS.

Mógłbym dać wersję wyświetlającą wynik, ale równie dobrze mogłaby wyglądać tak:

Kopiuj
for(int i = 0; i < 800000; i++)
{ printf("%d\n", 131.43); }

Więc jako test poprawności niezbyt by się przydało...
(Edit2: ofc u mnie dobrze wypisuje)

Edit: @O_o - Zamiana little endian <-> big endian niestety będzie zabójcza (tzn z szansy na błąd równej 0 zrobi się 1/0x10000). Wszystko dlatego że struktura musi być dostosowana do pobierania jej wartości przez wskaźniki czyli nie może być odwrócona w pamięci. Możnaby z tym walczyć przez kompilację warunkową albo trzymanie struktury jako 4 bajtowego inta i dobieranie się do elementów przez operacje bitowe ale... po co.
Teoretycznie też jakaś maszyna (w końcu celem projektowym C była przenośność więc na różnych szafach da się go uruchomić) mogłaby stosować niestandardowy zapis liczb zmiennoprzecinkowych ale nie potrafię do tego wymyślić realnej sytuacji.

edytowany 4x, ostatnio: msm
0

Popatrzyłem jeszcze w profilera, ciągle masakrycznie dużo zżerało synchronizowanie wątków, których przecież w tym programie nie ma...

W związku z tym poprawiłem trochę definicję listy, teraz wygląda przydługo:

Kopiuj
typedef std::list<Item *, boost::fast_pool_allocator<Item *, boost::default_user_allocator_new_delete, boost::details::pool::null_mutex> > StdItemStack;

ale dało to efekt (najnowsze wersje):
Delphi: 0.650 [s]
C: 0.19 [s]
C++: 0.27 [s]

Po tej zmianie wreszcie pod profilerem znaczenie ma kluczowa w tym programie funkcja - pow() (18%).

W załączniku źródła + binarka.

OO
E, masz może pomysł co te wątki tam robiły? ;)
msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:5 miesięcy
0

Niezły typ listy... Widzę że boost ma w zanadrzu sporo niezłych sztuczek.
Tak czy inaczej gratuluję :)

Teraz Delphi zostało samo... A nikt się chyba nie kwapi do optymalizowania go.

Ale trzeba przyznać uczciwie że 'normalnie' napisany kod Delphi był praktycznie równy 'normalnemu' (bez profilowania i optymalizacji) kodu C++.

edytowany 1x, ostatnio: msm
OO
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 13 lat
  • Postów:98
0

No to były cuda z tym Delphi tutaj :)
Ja męczę tego C#... Mogłem przepisywać kod Cepa a ja głupi zacząłem tłumaczyć z Delphi :( Koszmar :) Sądzę że jak już się skompiluje to i tak coś źle zadziała :/


O̾..͠o
OO
AARGHH! Przepisałem RPN.pas! Mój muuussskkk!
MA
  • Rejestracja:około 17 lat
  • Ostatnio:3 miesiące
  • Lokalizacja:Poznań
0

Nie powiedziałem ostatecznego słowa...
Później zedytuje (nie wiem kiedy bo żniwa :) )

Poddaję się :(. Napisałem proceduralnie, na tablicowych stosach i spadło do jedynie 680ms.

edytowany 2x, ostatnio: maciejmt
Zobacz pozostały 1 komentarz
msm
Niepotrzebnie się poddajesz, na pewno da się to zoptymalizować. Zauważ że na początku to Delphi wygrywał trzykrotnie i też wydawało się że nie da się C++ dużo bardziej poprawić :)
msm
Jak zdążę to przyjrzę się twojemu projektowi i pomyślimy co się da poprawić, może razem coś wymyślimy :)
OO
na razie się zamęczam żeby działało :D Sobie chwilowo odpoczywam bo zamiana unii na klasy była głupim pomysłem :x
msm
Ja mam jeszcze 2 pomysły jak to napisać. Też chwilowo odpoczywam bo sposób pierwszy miele mózg :S
OO
Można się pobawić też z [StructLayout(LayoutKind.Explicit)] i wtedy da się robić unie, zrobiłem tak kiedyś noobowski dostęp do poszczególnych bajtów integera. Inta to tylko wczytywałem i zapisywałem a operowałem na jego bajtach więc i tak bym rozdzielił do osobnych zmiennych a tak się robiło samo :)
OO
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 13 lat
  • Postów:98
0

Stringi w Delphi indeksuje się od 1? Bo jak tak to mam ten index co mi ucieka :>

Jej, dużo lepiej tym razem :>
Object reference not set to an instance of an object. ;)

Wo! Dałem && zamiast ||, ciapłem się w przerabianiu in [...]

A teraz dostaje wpiernicz batem za moje heretyczne zamienienie unii na klasy z dziedziczeniem x_x
Unable to cast object of type 'rpncsharp.itOp' to type 'rpncsharp.itVal'.

Łiiii.... Input string was not in a correct format. :/


O̾..͠o
edytowany 3x, ostatnio: O_o
MA
masz źródła przecież by Pascal : >
OO
No ale przepisałem tamto Expr[I] a tu bam bo C# ma stringi od 0 a Delphi od 1.
RE
Moderator
  • Rejestracja:około 18 lat
  • Ostatnio:12 miesięcy
2

U mnie:
C++: 161ms
C: 120ms
Delphi: 299ms, pierwsza wersja 670ms
C#: 350ms

Najnowsze wersje programów, moja wersja w C# jest właściwie kalką tego z C tylko na kolekcjach z .NET.
Użyłem także skróconej wersji atof, float.Parse, który bierze pod uwagę ustawienia kulturowe itd jest piekielnie wolny. Teraz najwolniejsze są kolekcje.
Sądzę, że gdyby się przepisało wersję C właściwie znak w znak włącznie z arytmetyką wskaźnikową i kolekcjami na nich, różnice byłyby naprawdę nieduże.

źródło C#: http://pastebin.com/r1R1n5j2

ciekawostka: fakt, trzeba było posiedzieć na tym algorytmem sporo czasu, żeby go zoptymalizować. Ale.. gdy dzisiaj czas programisty jest cenniejszy niż koszt podzespołów.. robi się Parallel.For(0, 800000, (i) => new RPN().Eval("2^3+123.43")); i z 350ms wynik spada nam na czterech rdzeniach do 110ms ;).

edytowany 1x, ostatnio: Rev
Zobacz pozostałe 2 komentarze
OO
Możesz wrzucić wszystkie binarki i kody na raz bo nie jestem pewien które ściągać :) O! Użyłeś [StructLayout(LayoutKind.Explicit)] :) A tak mnei kusiła a ja głupi polazłem w klasy :/
MA
tylko że zrównolegliłeś operacje a nie algorytm - w normalnych warunkach 800000 powtarzanie tego nie ma sensu ;D ale nowy rekordzik jest :>
OO
Normalny wynik wynosi C#: 350ms więc nie jest tak fajnie.
RE
A według mnie, zważywszy na to, że korzystamy z .NETowych kontenerów nie jest tak źle. W końcu raczej nikt się nie spodziewał, że będą szybsze niż zwykła arytmetyka wskaźnikowa.
OO
W sumie to ja nawet nie podejrzewałem że w .NET są Stack i Queue. Z lenistwa zadowalałem się zawsze użyciem List i Dictionary.
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 15 godzin
0
Kopiuj
        [StructLayout(LayoutKind.Explicit)]
        private struct Item
        {
            [FieldOffset(0)]
            public byte operatorType;
 
            [FieldOffset(1)]
            public byte priority;
 
            [FieldOffset(2)]
            public ushort itemType;
            
            [FieldOffset(0)]
            public float number;
        }

zaraz zaraz, przecież teraz number zakrywa resztę pól łącznie z itemType.

RE
No, o to przecież chodziło. To jedna ze sztuczek optymalizacyjnych MSM, na poprzedniej stronie masz wyjaśnienie.
0
MSM napisał(a)

Mógłbym dać wersję wyświetlającą wynik, ale równie dobrze mogłaby wyglądać tak:

Kopiuj
for(int i = 0; i < 800000; i++)
{ printf("%d\n", 131.43); }

Więc jako test poprawności niezbyt by się przydało...
(Edit2: ofc u mnie dobrze wypisuje)

Chodziło mi o to, że każda z wersji (Delphi, C++) wypisuje ostatni wynik - po to żeby potwierdzić że te 800000 liczeń robiło coś sensownego.

OO
A moja wersja C# przepisana z Delphi potrzebuje 300ms na jedno wykonanie pętli i wypisuje 0. :(
msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:5 miesięcy
0

Ciakawostka:

Kopiuj
if(op == '+' || op == '-')
    return 1;
else if (op == '*' || op == '/')
    return 2;
else
    return 3;

Skoro już optymalizujemy to na całego:

Kopiuj
return (op + 5) & 0x44;
Kopiuj
'+' = 0x2B  (+ 0x5 & 0x44 = ) 0x00
'-' = 0x2D  (+ 0x5 & 0x44 = ) 0x00
'*' = 0x2A  (+ 0x5 & 0x44 = ) 0x04
'/' = 0x2F  (+ 0x5 & 0x44 = ) 0x04
'^' = 0x5E  (+ 0x5 & 0x44 = ) 0x40

Różnice powinny zamknąć się w 1% ale i tak było warto. ;)

edytowany 4x, ostatnio: msm
OO
return (byte)((op + 5) & 0x44);
msm
To było w C :>
OO
Ale z rzutką działa też w C# i daje kopa gdzieś ze 1% :)
OO
  • Rejestracja:ponad 13 lat
  • Ostatnio:około 13 lat
  • Postów:98
0

Tak się bawię z wersją Rev'a:

  1. Przeniosłem do z Eval do konstruktora
Kopiuj
stack = new Stack<Item>();
queue = new Queue<Item>();

A do Eval dałem

Kopiuj
stack.Clear();
queue.Clear();

Przy wywołaniu w pętli jest szybsze lecz przy wywołaniu przez Parallel wolniej o_O


O̾..͠o
msm
Bo zysk z jednokrotnej inicjalizacji zżera synchronizacja.
MasterBLB
  • Rejestracja:około 19 lat
  • Ostatnio:około 17 godzin
  • Lokalizacja:Warszawa
  • Postów:1454
0

Chłopaki,czym dokładnie określiliście ten czas wykonania się?


"Sugeruję wyobrazić sobie Słońce widziane z orbity Merkurego, a następnie dupę tej wielkości. W takiej właśnie dupie specjalista ma teksty o wspaniałej atmosferze, pracy pełnej wyzwań i tworzeniu innowacyjnych rozwiązań. Pracuje się po to, żeby zarabiać, a z resztą specjalista sobie poradzi we własnym zakresie, nawet jeśli firma mieści się w okopie na granicy obu Korei."
-somekind,
konkretny człowiek-konkretny przekaz :]
Patryk27
Było napisane - QueryPerformanceCounter
MasterBLB
Ah racja,nie zauważyłem.Dzięki Patryk
vpiotr
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 3 lata
0

Dodaje wersję dla Lazarus Pascal - bazująca na wersji Delphi bez ASM (po drobnych przeróbkach).
Jak już ktoś wspomniał - ok. 2x wolniejsze od wersji w Delphi.

Próbowałem to sprofilować, ale pod Lazarusem chyba nie ma tak łatwo jak w Visual C++... (ulubiony GpProfile nie działa - brak INI).

Prawdopodobnie najwolniesze są alokacje pamięci...

To w sumie całkiem dobry test kompilatora i CPU...

msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:5 miesięcy
1

OK, zrobiło mi się żal C# więc postanowiłem go też lekko poprawić - w końcu mój ulubiony język :>

Start: 1.13
Wycięcie substringa: 1,03
Atof zmienia index : 0.95 (Wycięcie zbędnej pętli. Tego nie zrobiłem w C!)
Uruchomienie tego kodu jako release (hmm...) : 0.64
Zamiana char.IsDigit() na 2 porównania : 0.62 (różnica potwierdzona kilka razy, zawiodłem się na tobie optymalizatorze...)
Przeniesienie tworzenia stosu i kolejki do konstruktora (były tworzone i alokowane za każdym razem - rev, cóżeś uczynił?) : 0.37
Zamiana stosu na tablicę 0.31 - (do niewiernych - różnica jakby niewielka czyli te kolekcje nie są takie wolne... Zwłaszcza że wykonują dodatkowe sprawdzenie błędów :] )
Kolejka na tablicę (czyli coś czego domyślnie normalna kolejka nie może robić): 0.25
Kurczę, spodobała mi się zabawa, C++ @Cplusa już w tyle :> Spróbujemy dojść bliżej C
Switch na charze na switch na byte: 0.23 (to raczej nie jest przypadkowy wynik, różnica się potwierdza i chyba wiem czemu).
Jeszcze 2 char.IsDigit zmienione na 2 ify - 0.205
Jeszcze tylko troszeczkę :>
Ostatecznie: data.Length zmienione w atof na int len = data.Length - 0.195!
Ech, po ponad 20 minutach nie wymyśliłem żadnej dającej przyśpieszenie optymalizacji (pewnie z godzinę nad tym siedziałem :/ ) więc na razie niech tak będzie.

W sumie to doszedłem (prawie) do poziomu C :P Co prawda to użyłem dodatkowej sztuczki która w C się nie pojawiła ale fakt jest faktem - niskopoziomowcy, strzeżcie się!

PS. Mam jeszcze jeden pomysł na przyśpieszenie ale spróbuję tego później.

Oh my, zapomniałbym kodu wrzucić! http://pastebin.com/JSZshcG8

edytowany 5x, ostatnio: msm
Zobacz pozostałe 4 komentarze
vpiotr
Już znalazłem to słowo: "fatality" :)
RE
MA
widzę że się dalej jeszcze bawicie ;p Dlaczego nikt nie broni honoru Delphi ? :D
msm
Bo nikt nie zna :)
OO
Można by sportować zmiany które powstały podczas trwania turnieju ale to mało co zmieni.
RE
Moderator
  • Rejestracja:około 18 lat
  • Ostatnio:12 miesięcy
0

U mnie zoptymalizowana wersja C# wyciąga 102ms :). Jak dodamy wielowątkowość:

Kopiuj
Parallel.For<RPN>(0, 800000,
    () => new RPN(),
    (i, state, rpn) => {
        rpn.Eval("2^3+123.43");
        return rpn;
    },
    (rpn) => { result = rpn.Result; });

29ms
Może akurat w tym przypadku to trochę bez sensu, ale widać, że warto wielowątkowość stosować, a w nowym .NET za pomocą C# możemy coś takiego uzyskać jednym wywołaniem funkcji, możemy bez problemu określić stan początkowy (co by nie tworzyć 800 tys elementów, a 4), końcowy, etc.

msm
29 milisekund o_o? Nie spodziewałem się że aż taki przyrost będzie :) Parallel to jednak świetna rzecz... Btw. niezły masz procesor :)
RE
Procesor to czterordzeniowy i5-2400K na 4GHz. Właściwie dla PC średniej-wysokiej klasy to już standard.
OO
Wciąz niezły, ja mam też niby i5 tyle że M430, 2.27 na rdzeniu.
msm
To ja się nie odzywam ze swoim Core 2 Duo 2.8 Ghz :)
OO
Ale mam też Pentium I ze zintegrowanym radiatorem. :)
vpiotr
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 3 lata
0

W tym szybkim atof() z sieci był jeszcze jeden drobny błąd (brak inicjalizacji) - w załączniku lepsza wersja.
Zmieniłem też to na wersję "naiwną" - tą z C# i użyłem gdzieniegdzie naiwnego atoi(), ale to już niewiele pomogło:
Lazarus Pascal: 1.11 [s]

Delphi: 0.41 [s]
Delphi XE: 0.38 [s]

C++: 0.232 [s]
C: 0.18 [s]

Ten Parallel.For to rzeczywiście ciekawa sprawa, w C++ jest OpenMP i kilka bibliotek jednego-dostawcy (MS, Intel), ale nic tak bardzo uniwersalnego...

A ten żart to w sumie nie załapałem - bo nie znam C#... To jaka jest tak naprawdę tego wydajność?

[edit] - po mikro-optymalizacjach:
C++/VC2010: 0.224 [s]
C++/VC2010: 0.187 [s] - /fp:fast
C++/pgi 11.7: 0.44 [s] - (-w -fast -Mfprelaxed -Msafeptr -Mprefetch -Mnovect -alias=ansi -Mipa=fast,inline --zc_eh -tp barcelona-64 & acml::fastpow)
C++/gcc 4.4.1: 0.47 [s] - (-O3, -fexpensive-optimizations, -s, -ftree-vectorize, -march=amdfam10, -funroll-all-loops, -ffast-math -fomit-frame-pointer)
C++/intel 2011: 0.191 [s] (O3)
C++/Builder XE: 0.86 [s]

Z ciekawości sprawdziłem:
C#: 0.6 [s] - release, v.1
C#: 0.17 [s] - release, v.2

edytowany 22x, ostatnio: vpiotr
RE
Moderator
  • Rejestracja:około 18 lat
  • Ostatnio:12 miesięcy
0

Jak to żart ;P? No wydajność taka, jaką podajemy. W tym momencie faktycznie jest szybciej niż w C, ale gdyby wszystkie optymalizacje kodu były takie same (kod w 90% identyczny) to wydajność byłaby w gruncie rzeczy identyczna albo może i na lekką korzyść C#. Wszak JIT ma potencjalnie większe pole do popisu pod względem optymalizacji niż AOT.

msm
Z JITem jest jeden problem - musi się szybko wykonać. To sprawia że bardziej skomplikowane optymalizacje pod względem złożoności obliczeniowej są niemożliwe :(
msm
Administrator
  • Rejestracja:prawie 16 lat
  • Ostatnio:5 miesięcy
0

Wydajność jest taka jaką podawałem. Chodzi o to że pisałem to dla za zabawy a nie żeby udowodnić wyższość C# nad resztą świata (bo IMO mimo wszystko C/C++ jednak są szybsze tylko teraz im się nie udało :) ).

edytowany 1x, ostatnio: msm
MA
dla zabawy to napisz w Prologu :D
vpiotr
jak już sobie łamać głowę to na całego - proponuję T-SQL :)
vpiotr
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 3 lata
0

Ciekaw jestem na ile te wyniki były realne - i czy czasem C# sobie nie zredukował tych obliczeń do jednorazowego wykonania kalkulatora...

RE
Na pewno nie. Ale kompilator JIT mógł sobie np. przed pierwszym uruchomieniem programu dokładniej zanalizować ten kawałek kodu samego kalkulatora i odpowiednio go zoptymalizować.
vpiotr
  • Rejestracja:ponad 13 lat
  • Ostatnio:prawie 3 lata
0

Porównałem większość znanych mi kompilatorów C++ - (patrz strona 5).
Wyniki dowodzą, że nie tylko programista, ale i kompilator ma znaczenie - różnice potrafią być nawet 2x między różnymi kompilatorami i ich ustawieniami (np. dla pgi spadło m. ponad 1s do 0.4 s).

Szkoda, że GCC wypadło tak marnie...

Spróbuję jeszcze z najnowszymi C++ Builder i Delphi - jak mi przyślą klucze...

edytowany 1x, ostatnio: vpiotr
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)