[ASM] Rejestry xmm i tablice

CR
  • Rejestracja:ponad 16 lat
  • Ostatnio:11 miesięcy
0

Witam.

Umieściłbym temat w dziale newbie, bo to o co mam zamiar zapytać, zapewne dla obeznanych z tematem jest głupie i banalne, no ale takiego działu już nie ma... Do rzeczy. Piszę to jako ASMową wstawkę do Delphi. Mam tablicę złożoną z bajtów i chce ją modyfikować w pętli, używając rejestrów xmm (żeby było szybciej), jednak jako zupełnie początkujący w ASM nie mogę sobie z tym poradzić. Dla zwykłych rejestrów kod wyglądałby np. tak:

Kopiuj
lea eax, Tab //Tab to właśnie ta tablica
mov [eax], ZmiennaA
mov [eax + 1], ZmiennaB

itd.

Sęk w tym, że lea z rejestrami xmm działać nie chce i nie mam pojęcia jak wczytać do nich adres tej tablicy. Normalnie zaglądam sobie do debugera i od niego "zgapiam" jak rozwiązać pewne rzeczy, ale kompilator Delphi domyślnie rejestrow xmm nie używa.

I druga rzecz. Mógłby ktoś polecić jakieś dobre kurs źródła (po polsku lub angielsku) do nauki ASM pod x86? Chodzi mi o pisanie wstawek, więc nie muszę znać go perfekcyjnie, ale chciałbym opanować jakieś solidne podstawy.

edytowany 1x, ostatnio: Crow
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około godziny
0

lea jest do obliczania adresu, a w SSEx, AVX, AVX2 adres jest tylko jeden. Nie ma wektora adresów. Wektor adresów jest dopiero przy AVX-512 scatter i gather, ale wtedy i tak nie używasz instrukcji lea do wypełniania wektorów z adresami (przynajmniej nie bezpośrednio). lea nie jest też nikomu do niczego potrzebne w tym sensie, że można się obejść bez tego. W końcu jedyne co robi lea to proste instrukcje arytmetyczne. Jednak to lea możesz zostawić bo prawdopodobnie jest OK i wtedy zakładając że kod który podałeś jest OK:

Kopiuj
lea eax, Tab //Tab to właśnie ta tablica
mov [eax], ZmiennaA
mov [eax + 1], ZmiennaB

w wersji pod SIMD będziesz miał np:

Kopiuj
lea eax, Tab //Tab to właśnie ta tablica
movups [eax], WektorA
movups [eax + 16], WektorB

I druga rzecz. Mógłby ktoś polecić jakieś dobre kurs źródła (po polsku lub angielsku) do nauki ASM pod x86? Chodzi mi o pisanie wstawek, więc nie muszę znać go perfekcyjnie, ale chciałbym opanować jakieś solidne podstawy.

Jeśli klepiesz niskopoziomowy kod tylko okazjonalnie to lepiej używać tzw compiler intrinsics. Kompilator wtedy lepiej sprawdzi poprawność kodu i nie trzeba będzie w kółko debugować.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 5x, ostatnio: Wibowit
CR
  • Rejestracja:ponad 16 lat
  • Ostatnio:11 miesięcy
0
Wibowit napisał(a):

w wersji pod SIMD będziesz miał np:

Kopiuj
lea eax, Tab //Tab to właśnie ta tablica
movups [eax], WektorA
movups [eax + 16], WektorB

No właśnie nic z tego, przy linicje movups [eax], WektorA wywala mi "E2116 Invalid combination of opcode and operands".

Jeśli klepiesz niskopoziomowy kod tylko okazjonalnie to lepiej używać tzw compiler intrinsics. Kompilator wtedy lepiej sprawdzi poprawność kodu i nie trzeba będzie w kółko debugować.

Chciałbym, ale z tego co mi wiadomo, Delphi natywnie nie obsługuje SIMD i trzeba sobie samemu wstawki napisać.

edytowany 1x, ostatnio: Crow
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około godziny
0

Pokaż konkretny kod, który chcesz kompilować. Przy movups oba argumenty muszą być wektorami tego samego typu.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
CR
  • Rejestracja:ponad 16 lat
  • Ostatnio:11 miesięcy
0
Wibowit napisał(a):

Pokaż konkretny kod, który chcesz kompilować. Przy movups oba argumenty muszą być wektorami tego samego typu.

No właśnie jeszcze nie mam konkretnego kodu, tylko dłubię sobie takie przymiarki. Może opiszę co konkretnie chcę osiągnąć, to będzie łatwiej ogarnąć. Piszę sobie software'owy raster trójkątów. Najpierw wyznaczam prostokątną otoczkę trójkąta (bounding box), a następnie dzielę ją na segmenty (tiles) o wymiarach 8x8 pikseli i - po przeprowadzeniu wcześniejszych testów - wykonuję na każdym takim segmencie jedną z 3 operacji: Zostawiam w spokoju (nie robię nic), zamalowuję cały jednolitym kolorem, przeprowadzam dokładniejsze testy i zamalowuję segment piksel po pikselu. Czytałem w różnych opracowaniach, że przy tej operacji najlepiej skorzystać właśnie z SIMD, bo dzięki temu jeden taki segment można załatwić w jednym cyklu procesora. Z tego co wiem, Delphi nie posiada żadnych struktur, które by na to pozwalały i trzeba napisać własny kod w ASM, no więc staram się to ogarnąć.

U mnie to wygląda tak, że mam bufor klatki obrazu w postaci jednowymiarowej tablicy array of Integer (używam 32 bitowego formatu RGB). Chciałbym w pętli skakać do odpowiednich segmentów i wykonywać na nich wcześniej opisane operacje, możliwie jak najszybciej.

edytowany 3x, ostatnio: Crow
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około godziny
0
Crow napisał(a):

Najpierw wyznaczam prostokątną otoczkę trójkąta (bounding box), a następnie dzielę ją na segmenty (tiles) o wymiarach 8x8 pikseli i - po przeprowadzeniu wcześniejszych testów - wykonuję na każdym takim segmencie jedną z 3 operacji

Brzmi dość kiepsko. Dostęp do pamięci powinien być jak najbardziej liniowy żeby osiągnąć największą szybkość. W GPU stosuje się pewną technikę, która służy do upakowania sąsiednich pikseli do liniowej pamięci nie tylko dla relacji poziomej, ale też pionowej:
https://en.wikipedia.org/wiki/Z-order_curve#Texture_mapping

Texture mapping
Some GPUs store texture maps in Z-order to increase spatial locality of reference during texture mapped rasterization. This allows cache lines to represent rectangular tiles, increasing the probability that nearby accesses are in the cache. At a larger scale, it also decreases the probability of costly, so called, "page breaks" (i.e the cost of changing rows) in SDRAM/DDRAM. This is important because 3d rendering involves arbitrary transformations (rotations, scaling, perspective, and distortion by animated surfaces).
These formats are often referred to as swizzled textures or twidled textures. Other tiled formats may also be used.

Na CPU (podczas nakładania efektów na obrazy 2D) zwykle leci się całymi liniami i nie kombinuje z adresowaniem w taki sposób jak w GPU. Np jeśli masz obraz o wysokości 1000 pikseli to możesz go podzielić na 20 segmentów o wysokości 50 pikseli każdy, opakować każdy segment w osobne zadanie i wysłać te zadania do wykonania na puli wątków.

Jeśli chcesz wypełnić pamięć za pomocą SIMDa to:

  1. Najpierw musisz wypełnić rejestr SIMDowy pikselami
  2. Potem użyć tego rejestru do wypełnienia pamięci

"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 2x, ostatnio: Wibowit
CR
  • Rejestracja:ponad 16 lat
  • Ostatnio:11 miesięcy
0
Wibowit napisał(a):

Jeśli chcesz wypełnić pamięć za pomocą SIMDa to:

  1. Najpierw musisz wypełnić rejestr SIMDowy pikselami
  2. Potem użyć tego rejestru do wypełnienia pamięci

No i dokładnie to próbuję robić, tylko nie wiem jak ;/.

Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około godziny
0

Gdzie masz wypełanianie rejestru SIMDowego pikselami?


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
CR
  • Rejestracja:ponad 16 lat
  • Ostatnio:11 miesięcy
0
Wibowit napisał(a):

Gdzie masz wypełanianie rejestru SIMDowego pikselami?

Kopiuj
const
  Mask: array[0..15] of Byte = (10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 110, 120, 130, 140, 150);

var
  Tab: array[0..47] of Byte;

procedure Fill;
asm
  movups xmm0, Mask //Wypełnia rejestr zawartością maski
end;

I co dalej? Jak mam teraz przerzucić zawartość rejestru xmm0 do tablicy?

Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około godziny
0

Jeśli movups xmm0, Mask się kompiluje to spróbuj dać:

Kopiuj
movups xmm0, Mask
movups [Tab + 0], xmm0
movups [Tab + 16], xmm0
movups [Tab + 32], xmm0

Nie wiem jaka tam jest składnia tego asemblera. W składni Intela samo Mask powinno oznaczać adres, więc movups xmm0, Mask chyba nie powinno się kompilować. Zamiast tego powinno być movups xmm0, [Mask]. Kombinuj, ja tam nie mam Delphiego u siebie.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 1x, ostatnio: Wibowit
CR
  • Rejestracja:ponad 16 lat
  • Ostatnio:11 miesięcy
0
Wibowit napisał(a):

Jeśli movups xmm0, Mask się kompiluje to spróbuj dać:

Kopiuj
movups xmm0, Mask
movups [Tab + 0], xmm0
movups [Tab + 16], xmm0
movups [Tab + 32], xmm0

Od tego sam właśnie zacząłem (bo wydawało mi się to najbardziej logiczne, zwłaszcza, że ze zwykłymi rejestrami się kompiluje), ale nic z tego, wywala mi błąd: E2107 Operand size mismatch.

Kombinuj, ja tam nie mam Delphiego u siebie.

No staram się, ale właśnie nic nie chce działać, a że w ASM jestem cienki, to w akcie desperacji stworzyłem ten wątek ;(.

edytowany 2x, ostatnio: Crow
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około godziny
0

Myślałem że chociaż ta pierwsza instrukcja ci zadziałała. Napisałeś tak jakby działała. Spróbuj:

Kopiuj
movups xmm0, [Mask]
movups [Tab + 0], xmm0
movups [Tab + 16], xmm0
movups [Tab + 32], xmm0

"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
CR
  • Rejestracja:ponad 16 lat
  • Ostatnio:11 miesięcy
0
Wibowit napisał(a):

Myślałem że chociaż ta pierwsza instrukcja ci zadziałała. Napisałeś tak jakby działała. Spróbuj:

Kopiuj
movups xmm0, [Mask]
movups [Tab + 0], xmm0
movups [Tab + 16], xmm0
movups [Tab + 32], xmm0

Pierwsza działa i kompiluje się w obu postaciach:

Kopiuj
movups xmm0, [Mask]
movups xmm0, Mask

Natomiast już próby zapisywania zawartości rejestru do pamięci, nie ;/.

edytowany 1x, ostatnio: Crow
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około godziny
0

W C mi takie coś zadziałało: link
Na składni asma w Delphi się nie znam, a nie ma narzędzi online do odpalania kodu w Delphi.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
CR
  • Rejestracja:ponad 16 lat
  • Ostatnio:11 miesięcy
0

A co robi ten kod?

Kopiuj
mov ebx, 0x41424344; //Zapisuje do ebx ten adres?. Ale czemu akurat ten, czego to adres?
movd xmm0, ebx; //Przepisuje do xmm0 zawartość ebx?
punpckldq xmm0, xmm0; //Przestawia kolejność bitów? Ale po co?
punpcklqdq xmm0, xmm0; //Jak wyżej?
movups [%0 + 0], xmm0; //Zapisuje zawartość xmm0 pod adresem, ale czemu akurat tym, na co on wskazuje?
movups [%0 + 16], xmm0; //Jak wyżej
: "=r"(a) //Nie mam pojęcia
:
: "ebx" //Nie mam pojęcia
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około godziny
0
Kopiuj
mov ebx, 0x41424344; // zapisuje do ebx stałą zawierającą kody ASCII ciągu "ABCD"
movd xmm0, ebx; // kopiuje zawartość ebx do 32-najniższych bitów xmm0
punpckldq xmm0, xmm0; // duplikuje podwójne słowa, po tym 64-bity będą wypełnione stałą z początku
punpcklqdq xmm0, xmm0; // duplikuje poczwórne słowa, po tym całe 128-bitów będzie wypełnione stałą z początku
movups [%0 + 0], xmm0; // Zapisuje zawartość xmm0 pod adresem, a %0 to specjalna składnia GCC
movups [%0 + 16], xmm0; //Jak wyżej
// zarówno %0 jak i poniższe rzeczy są opisane na https://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html#s5
: "=r"(a)
:
: "ebx"

"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 2x, ostatnio: Wibowit
CR
A, czyli to nie przejdzie, bo takiej składni kompilator nie łyka.
CR
  • Rejestracja:ponad 16 lat
  • Ostatnio:11 miesięcy
0

"Odkopuję", bo udało mi się to rozgryźć i chciałem zapytać o coś jeszcze.

Miałem oryginalny kod:

Kopiuj
procedure TDisplay.Fill(const AColor: Cardinal);
var
  I: Cardinal;
  P: ^Cardinal;
begin
  P := @Buffer.Color[0];
  for I := 0 to Buffer.Size - 1 do
    begin
      P^ := AColor;
      Inc(P, 1);
    end;
end;

I przerobiłem go na:

Kopiuj
procedure TDisplay.FillASM(const AColor: Cardinal);
var
  Mask: array[0..3] of Integer;
asm
  lea ebx, Mask;
  mov edx, AColor;
  mov [ebx], edx;
  mov [ebx + 4], edx;
  mov [ebx + 8], edx;
  mov [ebx + 12], edx;
  movups xmm0, [ebx];
  mov ebx, [eax].Buffer.Color[0]; //Buffer.Color to jednowymiarowa tablica dynamiczna wypełniona integerami
  mov ecx, [eax].Buffer.Size; //Buffer.Size to jego szerokość pomnożona przez jego długość, np. 1920 x 1080
  shr ecx, 2;
  mov eax, 0;
@Loop:
  movups [ebx + eax], xmm0;
  add eax, 16;
  dec ecx;
  jnz @Loop;
end;

Różnica szybkości jest KOLOSALNA! pętla z 1000 powtórzeń w przypadku pierwszego kodu dawała 400-410 milisekund. Kod w ASM zamyka się w granicach... 27-28 ms! Próbowałem też wersji kodu, w której wykorzystałem więcej niż 1 rejestr, tzn coś takiego:

Kopiuj
procedure TDisplay.FillASM(const AColor: Cardinal);
var
  Mask: array[0..3] of Integer;
asm
  lea ebx, Mask;
  mov edx, AColor;
  mov [ebx], edx;
  mov [ebx + 4], edx;
  mov [ebx + 8], edx;
  mov [ebx + 12], edx;
  movups xmm0, [ebx];
  movups xmm1, xmm0;
  movups xmm2, xmm0;
  movups xmm3, xmm0;
  mov ebx, [eax].Buffer.Color[0];
  mov ecx, [eax].Buffer.Size;
  shr ecx, 4;
  mov eax, 0;
@Loop:
  movups [ebx + eax], xmm0;
  movups [ebx + eax + 16], xmm1;
  movups [ebx + eax + 32], xmm2;
  movups [ebx + eax + 48], xmm3;
  add eax, 64;
  dec ecx;
  jnz @Loop;
end;

Jednak nie miało to żadnego wpływu na szybkość działania. Jako że nie jestem zbyt biegły w ASM, nie jestem pewien z czym to jest związane. Czy chodzi o to, że CPU w jednym cyklu może dokonać tylko jednej operacji, polegającej na zapisie wartości w rejestrze do pamięci?
Drugie pytanie jest bardziej prozaiczne. Czy mój kody jest dobrze napisany? Wszystko optymalizuję tak jak trzeba? Mam wątpliwości zwłaszcza z tym zapisywaniem maski do rejestrów xmm, ale nie wiem, czy da się to zrobić lepiej. I czy maska w takiej formie jest konieczna? Jak samodzielnie alokować pamięć w ASM?

edytowany 2x, ostatnio: Crow
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około godziny
0

Jednak nie miało to żadnego wpływu na szybkość działania. Jako że nie jestem zbyt biegły w ASM, nie jestem pewien z czym to jest związane. Czy chodzi o to, że CPU w jednym cyklu może dokonać tylko jednej operacji, polegającej na zapisie wartości w rejestrze do pamięci?

Bardziej prawdopodobne, że przepustowość kontrolera pamięci jest wąskim gardłem. Spróbuj zamiast 16 bajtów naraz wypełniać 4-ema.

Mam wątpliwości zwłaszcza z tym zapisywaniem maski do rejestrów xmm, ale nie wiem, czy da się to zrobić lepiej. I czy maska w takiej formie jest konieczna?

Nie trzeba w ten sposób. Można się obejść bez dodatkowej pamięci (obojętne czy na stercie czy stosie). Zamiast kopiowania danych między pamięcią, a rejestrami można zastosować rozwiązanie z punpck...

Jak samodzielnie alokować pamięć w ASM?

Jeśli chcesz coś zaalokować na stercie to coś a'la malloc (tzn ten sam mechanizm co w języku goszczącym), a jeśli na stosie to arytmetyka na wskaźniku stosu (języki wyższego poziomu też to robią, ale to chowają przed programistą).

Czy mój kody jest dobrze napisany?

A nie wiem, nie ma jak sprawdzić, bo Delphi nie ma na żadnej testerce kodu źródłowego.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 2x, ostatnio: Wibowit
CR
  • Rejestracja:ponad 16 lat
  • Ostatnio:11 miesięcy
0
Wibowit napisał(a):

Nie trzeba w ten sposób. Można się obejść bez dodatkowej pamięci (obojętne czy na stercie czy stosie). Zamiast kopiowania danych między pamięcią, a rejestrami można zastosować rozwiązanie z punpck...

Mógłbym prosić o coś więcej? :). Jak tego użyć? Chciałem sprawdzić jak pamięć alokuje kompilator Delphi ale on odwołuje się do jakiegoś makra:

Kopiuj
mov eax X //gdzie X, tam ilość alokowanych bajtów
call @GetMem
mov [ebp-$08], eax

Nie wiem natomiast co robi wewnątrz tego makra i ja go wywołać w swoim kodzie nie mogę, bo kompilator krzyczy, że nie ma takiej etykiety (label) jak @GetMem.

Jeśli chcesz coś zaalokować na stercie to coś a'la malloc (tzn ten sam mechanizm co w języku goszczącym), a jeśli na stosie to arytmetyka na wskaźniku stosu (języki wyższego poziomu też to robią, ale to chowają przed programistą).

Teraz mam tak:

Kopiuj
asm
  push AColor;
  push AColor;
  push AColor;
  push AColor;

  movups xmm0, [esp];
  mov ebx, [eax].Buffer.Color[0];
  mov ecx, [eax].Buffer.Size;

  add esp, 16;
  shr ecx, 2;
  mov edx, 0;
@Loop:
  movups [ebx + edx], xmm0;
  add edx, 16;
  dec ecx;
  jnz @Loop;
end;

Dobrze to robię? Jest jakiś szybszy sposób na umieszczenie 16 bajtów na stosie, niż poprzez czterokrotny push (każdy zawiera 4-bajtowy kolor)?

A nie wiem, nie ma jak sprawdzić, bo Delphi nie ma na żadnej testerce kodu źródłowego.

Ja pytam o samą składnię i sposoby realizowania poszczególnych zadań (tj. czy np. nie wykonuję zbędnych czynności, które spowalniają działanie). Bo tak poza tym, to kod się kompiluje i działa.

edytowany 4x, ostatnio: Crow
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około godziny
0

Nie wiem natomiast co robi wewnątrz tego makra i ja go wywołać w swoim kodzie nie mogę, bo kompilator krzyczy, że nie ma takiej etykiety (label) jak @GetMem

Spróbuj zamiast @GetMem dać samo GetMem. Czy to GetMem działa poza kodem asmowym?

Dobrze to robię? Jest jakiś szybszy sposób na umieszczenie 16 bajtów na stosie, niż poprzez czterokrotny push (każdy zawiera 4-bajtowy kolor)?

Już napisałem, żeby użyć punpck... i nie bawić się w kopiowanie pamięci poza rejestrami. Już podałem link do kodu w C++ więc można go przenieść do Delphi. Np zamiast

Kopiuj
  lea ebx, Mask;
  mov edx, AColor;
  mov [ebx], edx;
  mov [ebx + 4], edx;
  mov [ebx + 8], edx;
  mov [ebx + 12], edx;
  movups xmm0, [ebx];

zrobić:

Kopiuj
  mov edx, AColor;
  movd xmm0, edx;
  punpckldq xmm0, xmm0;
  punpcklqdq xmm0, xmm0;

a może nawet zadziała:

Kopiuj
  movd xmm0, AColor;
  punpckldq xmm0, xmm0;
  punpcklqdq xmm0, xmm0;

Jeśli chcesz się dowiedzieć co robią te instrukcje to zobacz np na to: http://qcd.phys.cmu.edu/QCDcluster/intel/vtune/reference/vc265.htm

Ja pytam o samą składnię i sposoby realizowania poszczególnych zadań (tj. czy np. nie wykonuję zbędnych czynności, które spowalniają działanie). Bo tak poza tym, to kod się kompiluje i działa.

Spowalnianie to jedno, ale ważniejsza jest poprawność. Dla przykładu trzeba poszukać które rejestry można bezkarnie nadpisywać, a które trzeba przywracać przed powrotem z kodu asmowego lub przed wywołaniem procedur.

Kopiuj
 mov ebx, [eax].Buffer.Color[0]; //Buffer.Color to jednowymiarowa tablica dynamiczna wypełniona integerami
...
 movups [ebx + eax], xmm0;

Co tu się dzieje? Do ebx wstawiasz kolor, a potem używasz tego koloru jako adresu bazowego? Dziwnie to wygląda.

Testowałeś w ogóle poprawność swojego kodu? W sensie sprawdzenia czy nadpisuje dokładnie tyle pamięci ile trzeba, ani mniej ani więcej.


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
edytowany 4x, ostatnio: Wibowit
CR
Co do rejestrów to znalazłem tyle, że można dowolnie nadpisywać te podstawowe, a także rejestry stosu i xmm (bo podstawowe i stosu kompilator sam zapisuje przed wywołaniem ASMowej wstawki, a xmm nie używa). Inne trzeba ręcznie zapisywać i przywracać. Ogólnie dokumentacja ASM do Delphi leży i kwiczy. Gdyby nie zewnętrzne źródła, nigdy bym nie wpadł na to, że w przypadku metod ukryty parametr self jest umieszczany zarówno na stosie, jak i w eax i to niezależnie od konwencji wywołania.
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)