Wykorzystanie instrukcji wektorowych (MMX) w asemblerze

Wykorzystanie instrukcji wektorowych (MMX) w asemblerze
IN
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:47
0

Cześć, jestem w trakcie pisania programu tworzącego negatyw bitmapy. Funkcja, która wykonuje ten negatyw pobiera jednowymiarową tablicę bajtów, rozmiary tablicy oraz zmienną do której zapisuje ilość cykli procesora. Wygląda mniej więcej tak:

Kopiuj
Negatyw24 PROC stdcall uses eax ebx ecx edx, tab :dword, amount :dword, cycles :dword

	RDTSC
	mov ECX, cycles				;pobranie adresu tablicy
	mov [ECX], EDX				;zapis starszej polowki licznika
	mov [ECX+4], EAX			;zapis mlodszej polowki licznika
	
	mov EAX, tab	;kopiuj adres 1 komorki
	add EAX, amount	;dodaj ilosc komorek
	sub EAX, 1		;przjedz do ost komorki
loop:
	mov BL, [EAX]	;pobierz komorke do rej
	mov CL, 255		;laduj FF do CL
	sub CL, BL		;neguj bajt w BL
	mov [EAX], CL	;zapisz zaneg bajt do pao
	cmp EAX, tab	;sprawdz koniec tablicy
	je end
	sub EAX, 1		;przesun sie w tablicy o 1 komorke do tylu
	jmp loop
end:	
	RDTSC						;dokonaj drugiego odczytu licznika cykli
	mov ECX, cycles	
	
	mov EBX, [ECX]				;odczytaj z pamieci poprzednie pomiary
	mov EBX, [ECX+4]
	
	sub EAX, [ECX+4]		;oblicz roznice
	sbb EDX, [ECX]		
	mov [ECX], EDX			;i zapisz rezultat do kolejnych dwoch pol tablicy
	mov [ECX+4], EAX

    ret 

Negatyw24 ENDP

Aktualnie mój kod napisany jest bez wykorzystania instrukcji procesora MMX. Czy byłby ktoś w stanie tak przerobić kod odpowiedzialny za samo wykonanie negatywu:

Kopiuj
mov EAX, tab	;kopiuj adres 1 komorki
	add EAX, amount	;dodaj ilosc komorek
	sub EAX, 1		;przjedz do ost komorki
loop:
	mov BL, [EAX]	;pobierz komorke do rej
	mov CL, 255		;laduj FF do CL
	sub CL, BL		;neguj bajt w BL
	mov [EAX], CL	;zapisz zaneg bajt do pao
	cmp EAX, tab	;sprawdz koniec tablicy
	je end
	sub EAX, 1		;przesun sie w tablicy o 1 komorke do tylu
	jmp loop
end:	

aby wykorzystać te instrukcje MMX? Może to być zrobione sztucznie, czyli zamiast int stosować float, cokolwiek. Wszelkie moje próby kończyły się niepowodzeniem, stąd prośba do Was. Niestety mam takie wymagania projektowe i muszę to tak zrobić.

edytowany 2x, ostatnio: Infinito
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 10 godzin
0

To pokaż te próby.

Jest instrukcja PSUBB do odejmowania wektorów złożonych z bajtów, ale do celu negatywu wystarczy PXOR z samymi jedynkami (czyli negowanie bitów po prostu).

Wykorzystując instrukcje wektorowe musisz pamiętać o obsłużeniu przypadku, gdy rozmiar danych wejściowych nie jest wielokrotnością rozmiaru sprzętowego wektora. W takim przypadku robisz wektorowo tyle ile się da, a resztkę na końcu obsługujesz kodem szeregowym.


"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.
IN
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:47
0

Nie mam tych prób bo je usunąłem i przywróciłem stary kod skoro nie działały. A mógłbym prosić o przykład z użyciem PXOR?

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

Może zrobię to inaczej.

Masz kod typu:

Kopiuj
// lecimy sobie po jednym bajcie
for (int i = 0; i < size; i++) {
  tab[i] = 255 - tab[i];
}

Najpierw musisz go przerobić na kod typu:

Kopiuj
// lecimy sobie po 8 bajtowych kawałkach
for (int i = 0; i < size / 8; i++) {
  for (int j = i * 8; j < (i + 1) * 8; i++) {
    tab[j] = 255 - tab[j];
  }
}
// lecimy po końcówce która nie utworzyła 8 bajtowego kawałka
for (int i = size / 8 * 8; i < size; i++) {
  tab[j] = 255 - tab[j];
}

Teraz można zastąpić wewnętrznego fora instrukcjami MMX:

Kopiuj
vec8b mask = same jedynki;
for (int i = 0; i < size / 8; i++) {
  // załadowanie wektora danymi od pozycji i * 8 do i * 8 + 7 włącznie
  vec8b input = tab[i * 8 : i * 8 + 7];
  vec8b result = PXOR(input, mask);
  tab[i * 8 : i * 8 + 7] = result
}
// lecimy po końcówce która nie utworzyła 8 bajtowego kawałka
for (int i = size / 8 * 8; i < size; i++) {
  tab[j] = 255 - tab[j];
}

"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.
IN
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:47
0

Dobra, zrozumiałem już ideę działania tego, ale niestety napisanie tego w asemblerze trochę przerasta moje umiejętności. Mógłbym prosić o przykład w asm?

Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 20 godzin
2

Wymodziłem na szybko coś takiego:

Kopiuj
#include <mmintrin.h>

void negatyw(unsigned char *tab, size_t len)
{
	size_t len8 = len / 8;

	_mm_empty();
	__m64 *mtab = (__m64*)tab;
	__m64 ones = _mm_set_pi32(0xFFFFFFFF, 0xFFFFFFFF);

	// główna część
	for (size_t i = 0; i < len8; i++)
	{
		mtab[i] = _mm_xor_si64(mtab[i], ones);
	}
	_mm_empty();

	// końcówka
	for (size_t i = len8 * 8; i < len; i++)
	{
		tab[i] = ~tab[i];
	}
}

int main()
{
	unsigned char tablica[999];
	negatyw(tablica, sizeof(tablica));
	return 0;
}

Nie sprawdzałem poprawności działania, kod się kompiluje i nie zawiesza. Wygenerowany kod w asemblerze wygląda tak:

Kopiuj
00A6101A  emms  
00A6101C  mov         dword ptr [esp+4],0FFFFFFFFh  
00A61024  xor         eax,eax  
00A61026  mov         dword ptr [esp],0FFFFFFFFh  
00A6102D  movq        mm2,mmword ptr [esp]  
00A61031  movq        mm1,mmword ptr [esp+eax*8+8]  
00A61036  pxor        mm1,mm2  
00A61039  movq        mmword ptr [esp+eax*8+8],mm1  
00A6103E  inc         eax  
00A6103F  cmp         eax,7Ch  
00A61042  jb          main+31h (0A61031h)  
00A61044  emms  
00A61046  mov         eax,3E0h  
00A6104B  jmp         main+50h (0A61050h)  
00A6104D  lea         ecx,[ecx]  
00A61050  not         byte ptr [esp+eax+8]  
00A61054  inc         eax  
00A61055  cmp         eax,3E7h  
00A6105A  jb          main+50h (0A61050h)  

Możesz sobie go postudiować.

edytowany 3x, ostatnio: Rev
IN
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:47
0

Dziękuję, spróbuję teraz sobie z tym poradzić, powinno być łatwiej.

IN
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:47
0

A jakbym chciał wykonać to samo, tyle że na instrukcjach SSE to będzie wyglądać analogicznie?

Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 20 godzin
1

pod SSE:

  • nie używa się instrukcji emms
  • rejestry nazywają się xmm1 a nie mm1
  • rejestry mają 128 bitów (16 bajtów) a nie 64 bity (8 bajtów)
  • dane powinny być wyrównane do granicy 16 bajtów (pod MMX - 8 bajtów)
  • większość instrukcji nazywa się i działa tak samo

wyrównanie pod visualem można osiągnąć pisząc __declspec(align(16)) unsigned char tab[N];
jeśli wyrównanie jest nieosiągalne, to trzeba dodać na początku pętlę ze zwykłym działaniem podobną do tej końcowej.

Kopiuj
void negatyw(unsigned char *tab, size_t len)
{
    size_t lenm = len / 16;
 
    __m128i *mtab = (__m128i*)tab;
    __m128i ones = _mm_set1_epi8((char)0xFF);
 
    // główna część
    for (size_t i = 0; i < lenm; i++)
    {
        mtab[i] = _mm_xor_si128(mtab[i], ones);
    }
 
    // końcówka
    for (size_t i = lenm * 16; i < len; i++)
    {
        tab[i] = ~tab[i];
    }
}

Jakoś tak. Nie sprawdzane, więc kodu w asm też nie podam. Z tego co pamiętam, zamiast movq (move quadword) trzeba napisać movdqa (move double quadword aligned).

Generalnie polecam używanie funkcji "intrinsic" w C zamiast babranie się bezpośrednio w asemblerze. Jak widzisz po przykładzie wyżej, kod wygenerowany przez kompilator (przy maksymalnej optymalizacji i w trybie release) jest całkiem czysty.

edytowany 3x, ostatnio: Azarien
IN
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:47
0

Jakbym nie musiał to w ogóle bym tego w asemblerze nie robił, ale że taki projekt to cóż poradzić :) dzięki Ci bardzo, będę walczył!

IN
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:47
0

Cześć, starałem się coś zrobić z tymi instrukcjami wektorowymi (SSE konkretnie) ale cały czas dostaje albo heap corruption albo "program przestał działać"... :( Pomożecie jeszcze? Aktualny kod asm:

Kopiuj
;-------------------------------------------------------------------------
.686 
.mmx
.xmm
.MODEL flat, stdcall


OPTION CASEMAP:NONE

INCLUDE    include\windows.inc
INCLUDE    include\user32.inc
INCLUDE    include\kernel32.inc 
  
.CODE

DllEntry PROC hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD

    mov eax, TRUE  
    ret

DllEntry ENDP

;-------------------------------------------------------------------------

Negatyw24 PROC stdcall uses eax ebx ecx edx edi esi, tab :dword, amount :dword
	
	mov EAX, tab	;adres początku tablicy
	add EAX, amount ;dodaj ilosc komorek
	sub EAX, 1		;przejdz do ostatniej komorki

	pcmpeqd xmm0, xmm0
	
	; -----------------------
	; Petla negowania z SSE -
	; -----------------------

	petlaSSE:
		cmp EAX, tab	;jesli koniec
		je koniecSSE	;to koniec instrukcji SSE

		movaps xmm1, [EAX]
		movaps xmm1, [EAX-1]
		movaps xmm1, [EAX-2]
		movaps xmm1, [EAX-3]
		movaps xmm1, [EAX-4]
		movaps xmm1, [EAX-5]
		movaps xmm1, [EAX-6]

		pxor	xmm1, xmm0					; zanegowanie wartości rejestrów
		pxor	xmm2, xmm0
		pxor	xmm3, xmm0
		pxor	xmm4, xmm0
		pxor	xmm5, xmm0
		pxor	xmm6, xmm0
		pxor	xmm7, xmm0

		movaps [EAX], xmm1
		movaps [EAX-1], xmm2
		movaps [EAX-2], xmm3
		movaps [EAX-3], xmm4
		movaps [EAX-4], xmm5
		movaps [EAX-5], xmm6
		movaps [EAX-6], xmm7

		sub EAX, 7	;zmniejszenie komorek o 7

		jmp petlaSSE

	koniecSSE:	

    ret 

Negatyw24 ENDP

;-------------------------------------------------------------------------

END DllEntry
edytowany 1x, ostatnio: Infinito
Azarien
jak to kompilujesz... jak linkujesz... w przypadku asma to nie jest takie proste zgadnąć co to za asembler w ogóle i jak to skompilować.
IN
Wszystko robię za pomocą Visual Studio 2013 Ultimate więc masm, funkcja w ASM jest utworzona w bibliotece DLL, która jest ładowana i używana w głównym programie.
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 20 godzin
1
Kopiuj
        movaps xmm1, [EAX]
        movaps xmm1, [EAX-1]
        movaps xmm1, [EAX-2]
        movaps xmm1, [EAX-3]
        movaps xmm1, [EAX-4]
        movaps xmm1, [EAX-5]
        movaps xmm1, [EAX-6]

na pewno? po co do tyłu, dlaczego indeksujesz po jednym bajcie, i do jednego rejestru? ;-)

poza tym mówiłem o instrukcji movdqa, skąd nagle movaps?

Wszystko robię za pomocą Visual Studio 2013 Ultimate więc masm,

a musi być ten asm? Visual ma gotowe funkcje do operowania na MMX i SSE, co zresztą pokazałem.

edytowany 2x, ostatnio: Azarien
IN
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:47
0

Co do indeksowania po 1 bajcie to akurat tutaj tak testowałem, robiłem -16, -32 itd. też nie działało. W dół dlatego, że lecę od końca tablicy w dół, ale w sumie to jest obojętne. Co do jednego rejestru to faktycznie... ślepy niczym kret jestem przepraszam :(
Movaps znalazłem w internecie, zmienię na movdqa :)
ASM niestety musi być bo taki mam projekt na studiach i muszę tak to zrobić niestety.

Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 10 godzin
1

Jak będziesz indeksował po jednym bajcie to dostaniesz banialuki. Masz skalować o rozmiar rejestru. I pamiętaj też o tym:

When the source or destination operand is a memory operand, the operand must be aligned on a 16-byte boundary or a general-protection exception (#GP) will be generated.

Czyli nie [eax - 5] tylko [eax - 5 * 16]. Nie sub eax, 7 tylko sub eax, 7 * 16.


"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
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 20 godzin
0

Zaczynaj od początku tablicy nie od końca, bo tak łatwiej jest dopilnować wyrównania. No i jeśli rozmiar tablicy nie jest podzielny przez ilość bajtów obrabianych w jednym przebiegu pętli, to końcówkę trzeba przerobić klasycznie (czyli bez SSE).

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

Jeśli tablica nie jest wyrównana, to początek też trzeba zrobić bez SSE.


"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.
Azarien
tak, ale łatwiej jest narzucić wyrównanie początku niż końca.
IN
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:47
0

Zmieniłem to tak i starałem się dodać to wyrównanie na początku ale coś średnio mi wyszło raczej.
A oto kod:

Kopiuj
Negatyw24 PROC stdcall uses eax ebx ecx edx edi esi, tab :dword, amount :dword

	mov EAX, tab	;adres początku tablicy
	add EAX, amount ;dodaj ilosc komorek
	sub EAX, 1		;przejdz do ostatniej komorki

	pcmpeqd xmm0, xmm0

	unaligned:
		not byte ptr [eax]
		add eax, 1
		sub ecx, 1

		test al, 15
		jnz unaligned
	
	; -----------------------
	; Petla negowania z SSE -
	; -----------------------

	petlaSSE:
		cmp EAX, tab	;jesli zostalo mniej niz 112 bajtow
		jb koniecSSE	;to koniec instrukcji SSE

		movdqa xmm1, [EAX]
		movdqa xmm2, [EAX-16]
		movdqa xmm3, [EAX-32]
		movdqa xmm4, [EAX-48]
		movdqa xmm5, [EAX-64]
		movdqa xmm6, [EAX-80]
		movdqa xmm7, [EAX-96]

		pxor	xmm1, xmm0					; zanegowanie wartości rejestrów
		pxor	xmm2, xmm0
		pxor	xmm3, xmm0
		pxor	xmm4, xmm0
		pxor	xmm5, xmm0
		pxor	xmm6, xmm0
		pxor	xmm7, xmm0

		movdqa [EAX], xmm1
		movdqa [EAX-16], xmm2
		movdqa [EAX-32], xmm3
		movdqa [EAX-48], xmm4
		movdqa [EAX-64], xmm5
		movdqa [EAX-80], xmm6
		movdqa [EAX-96], xmm7

		sub EAX, 112	

		jmp petlaSSE

	koniecSSE:	

        ret 

Negatyw24 ENDP

Teraz dostaje taki oto komunikat:
user image

edytowany 1x, ostatnio: Infinito
IN
Najgorsze jest to, że nie mogę debugować DLL z ASM bo debugger visuala w ogóle nie wchodzi do funkcji asm z racji tego, że nie widzi "symboli" czyli pliku .pdb... Więc robię trochę na ślepo.
Azarien
no to wygeneruj ten pdb.. ml /Zi
IN
pdb jest wygenerowany dokładnie w tym samym miejscu gdzie pdb z głównego projektu jak i plik .exe
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 10 godzin
0

Jeśli np ostatni bajt tablicy jest pod adresem równym 1 modulo 16 to wtedy pierwsza pętla (o etykiecie unaligned) cofnie się o jeden bajt, a następna (petlaSSE) podmieni bajty za końcem tablicy. Ponadto zamiast cmp EAX, tab powinno być coś w stylu cmp EAX, tabMinus112, a następnie powinna być pętla do przetwarzania końcówki.


"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.
IN
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:47
0

@Wibowit Średnio rozumiem twoje pierwsze zdanie i też nie wiem jak by temu zaradzić. Co do tego porównania to masz rację, zmieniłem to i teraz wygląda tak. Jednakże program ogólnie dalej się crashuje i nie wiem jak to zmienić żeby było dobrze.

Kopiuj
	mov EAX, tab	;adres początku tablicy
	mov EBX, tab
	add EBX, amount ;uzyskaj ostatnia komorke
	sub EBX, 112	;zaladowanie maksymalnej liczby do SSE

	pcmpeqd xmm0, xmm0

	unaligned:
		not byte ptr [eax]
		add eax, 1
		;sub ecx, 1

		test al, 15
		jnz unaligned
	
	; -----------------------
	; Petla negowania z SSE -
	; -----------------------

	petlaSSE:
		cmp EAX, EBX	;jesli zostalo mniej niz 112 bajtow
		jnb koniecSSE	;to koniec instrukcji SSE
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 10 godzin
0

Zmieniłeś jb na jnb i niby ma działać tak samo?

W pierwszym zdaniu chodziło mi o to, że o ile w pierwszej pętli cofasz się do granicy 16 bajtów, to jeżeli tablica nie kończy się na granicy 16 bajtów to w drugiej pętli na samym początku dobierasz się do bajtów spoza tablicy. movdqa xmm1, [EAX] ładuje bajty od eax do eax+15 włącznie i powinieneś widzieć od razu, że to może oznaczać dobieranie się do bajtów spoza tablicy.


"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.
IN
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:47
0

Zmieniłem to, że do rejestru EBX rozmiar tablicy i odejmuje od niego 112 czyli sumaryczny rozmiar rejestrów MMX, natomiast w EAX zostaje początek tablicy. No i teraz tutaj

Kopiuj
        cmp EAX, EBX    ;jesli zostalo mniej niz 112 bajtow
        jnb koniecSSE    ;to koniec instrukcji SSE

wykona skok, jeśli nie mniejszy, czyli jeśli przekroczy możliwy zakres wykonania dla MMX, czyli resztę trzeba ręcznie zanegować. Tak to rozumiem.

A co do drugiego to rozumiem i to faktycznie może być przyczyną tych błędów, ale jak temu zaradzić? Bo jeśli np cofnie się 5 razy to później dobiorę się do 5 miejsc spoza tablicy...

Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 10 godzin
2

Podam algo w pseudo C. Instrukcja mem(a) dobiera się do bajtu po adresem a.

Kopiuj
int a = <adres tab>;
int n = <rozmiar>;
int i = a;
while (i < min(a + n, ((a - 1) | 15) + 1)) {
  // tutaj lecimy skalarnie, bez użycia SSE
  mem(i) ^= -1;
  i++;
}
while (i <= a + n - 7 * 16) {
  // te instrukcje oczywiście trzeba zamienić na wektorowe, 7 wektorów SSE
  mem(i) ^= -1;
  ...
  mem(i + 7 * 16 - 1) ^= -1;
  i += 7 * 16;
}
while (i < a + n) {
  // tutaj lecimy skalarnie, bez użycia SSE
  mem(i) ^= -1;
  i++;
}

"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
IN
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:47
0
Kopiuj
while (i < min(a + n, ((a - 1) | 15) + 1)) {

Kompletnie tego nie rozumiem... :( szukam minimum czy jak?
Czy mógłbym Cię prosić o napisanie tego kodu w asemblerze?

Także nie rozumiem jednej rzecy... skoro w tym moim kodzie jeśli nie jest wyrównany do 16 to dodaje 1 do EAX, to czy nie powinno po prostu pominąć kilka początkowych bajtów i wykonać się dalej? W sensie ta negacja w rejestrach MMX?

edytowany 3x, ostatnio: Infinito
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 10 godzin
1

a + n to adres końca tablicy, a ((a - 1) | 15) + 1 to pierwszy adres podzielny przez 16 nie wcześniejszy niż a. min jest po to, by nie wyjechać poza tablicę.

Także nie rozumiem jednej rzecy... skoro w tym moim kodzie jeśli nie jest wyrównany do 16 to dodaje 1 do EAX, to czy nie powinno po prostu pominąć kilka początkowych bajtów i wykonać się dalej? W sensie ta negacja w rejestrach MMX?

Nie wiem do czego pijesz, ale jeśli chodzi o sens mojego kodu to:

  • najpierw wyobraź sobie tablicę idealnie wyrównaną do granicy 16 bajtów, zarówno na początku jak i na końcu,
  • taką tablicę można ogarnąć SSE bez problemu,
  • jednak po przeniesieniu początku o kilka bajtów wstecz, a końca kilka bajtów wprzód już nie będzie można tego ogarnąć SSE,
  • trzeba więc zrobić trzy pętle - jedna do ogarnięcia niewyrównanego początku, jedna dla szybkiego przechodzenia środka za pomocą SSE i jedna do ogarnięcia niewyrównanej końcówki.
    Mój kod to wszystko robi.

"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
IN
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:47
0

Ok już rozumiem. Ale wychodzi na to, że nie muszę robić tego wyrównania na początku bo on i tak zawsze zaczyna od początku tej tablicy. Udało mi się to zrobić, jedyne co mi pozostało to zrobienie bez SSE końcówki tablicy, tej która jest za mała na rejestry XMM, ale to myślę, że jutro zrobię :)

Napiszę czy udało mi się to dokończyć, myślę, że już pójdzie z górki. Bardzo wam dziękuję za pomoc! :) Jakby coś to będę się jeszcze pytał :)

Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 20 godzin
0

Ale wychodzi na to, że nie muszę robić tego wyrównania na początku bo on i tak zawsze zaczyna od początku tej tablicy.

Początek tablicy też musi być wyrównany a domyślnie nie jest to zagwarantowane.

Można wymusić prawidłowe wyrównanie tablicy

Kopiuj
__declspec(align(16)) unsigned char tablica[9999];

Wtedy nie jest potrzebna początkowa pętla i kod się uprości. Ale nie zawsze jest to wykonalne.

edytowany 2x, ostatnio: Azarien
Wibowit
  • Rejestracja:prawie 20 lat
  • Ostatnio:około 10 godzin
0

Ale sama tablica nie musi się zaczynać od adresu podzielnego przez 16, a o to właśnie chodzi. Procesora nie obchodzi co czym jest (w sensie czy tablicą, wskaźnikiem na element, itd), on po prostu oblicza adres w pamięci i ten adres musi być podzielny przez 16 jeśli używasz SSE.


"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
IN
  • Rejestracja:ponad 12 lat
  • Ostatnio:ponad 4 lata
  • Postów:47
0

Dobrze, to w takim razie będę jutro testował mój program i dorobię wyrównanie. Dam znać o efektach.

IN
Udało mi się wszystko zrobić, przetestowane dla różnych bitmap. Dziękuję wam bardzo za nieocenioną pomoc :)
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)