Mais um blog inútil.

Coding

Abril 29, 2009

Byte Swap com SSE4.1

Arquivado em: assembly, coding, useless — dcoder @ 17:03

Para finalizar esta série de optimizações inúteis, trago-vos a versão final desta função:

BITS 32

%define UNROLL_COUNT (4)

section .data
align 16
shuffle: dd 0x04050607, 0x00010203, 0x0c0d0e0f, 0x08090a0b

section .text
global sse41_bswap64
sse41_bswap64:
  push ebp
  mov edx, [esp+8] ; buffer -- assumed aligned 16
  mov ecx, [esp+12] ; length in #words
  test   ecx, ecx
  jz  near   _end

  and    ecx, -(UNROLL_COUNT*2) ; make ecx even
  jz     _finalize

  movdqu xmm7, [shuffle]
align 16
_loop:
  sub ecx, UNROLL_COUNT*2
  ; use movntdqa with sse 4.1   
  movntdqa xmm0, [edx + 00]
  movntdqa xmm1, [edx + 16]
  movntdqa xmm2, [edx + 32]
  movntdqa xmm3, [edx + 48]

  pshufb xmm0, xmm7 ; p5, 1l 1t
  pshufb xmm1, xmm7 ; p5, 1l 1t
  pshufb xmm2, xmm7 ; p5, 1l 1t
  pshufb xmm3, xmm7 ; p5, 1l 1t

  ; use movntdq --- the data won't be accessed again 
  ; until the end of the function
  movntdq [edx + 00], xmm0
  movntdq [edx + 16], xmm1
  movntdq [edx + 32], xmm2
  movntdq [edx + 48], xmm3

  lea edx, [edx+ 16*UNROLL_COUNT];
  jnz _loop ; no dependency, all flags were 
             ; computed in the beginning of the loop
_finalize:
  sfence
  mov  ebp, [esp+8]
  and  ebp, (UNROLL_COUNT*2)-1 ; ebp = count mod UNROLL
  jz _end

_endloop:
  mov eax, [edx]
  bswap eax ; p0+p5
  mov ecx, [edx+4]
  bswap ecx
  mov [edx], ecx
  mov [edx+4], eax
  sub ebp, 1
  lea edx, [edx+8]
  jnz _endloop
_end:
  pop ebp
  ret

A única diferença nesta é que agora não apenas os stores, mas também os loads são não temporais, evitando a poluição da cache com dados que sabemos que nao vão ser usados. A performance desta última versão é a melhor do grupo, como demonstrado:

[dcoder@localhost bswap]$ ./bswap 
ref done
sse2 done
ssse3 done
SSE4.1 done
Ref  : 1804875336 cycles
SSE2 : 1822536180 cycles
SSSE3: 1012685742 cycles
SSE41: 998087949 cycles
Speedup: 44.700449%

Byte Swap com SSSE3

Arquivado em: assembly, coding, serious-business, useless — dcoder @ 04:26

Lembrei-me há pouco, durante as minhas insónias, que podemos aproveitar uma nova instrução introduzida nos Core 2 para acelerar consideravelmente esta operação: PSHUFB.

Essencialmente, o PSHUFB permite-nos criar uma permutação à escolha dentro de um registo XMM. É fácil ver como isto se aplica no nosso caso a inversão de bytes.

Para aumentar o débito, nas escritas utilizo a instrução MOVNTDQ, que dá a dica ao processador que a memória em causa já não vai ser acedida em breve. SFENCE serve para serializar todos estes armazenamentos. Mais uma vez leio/escrevo uma cache line por iteração (podia abusar e fazer isto para uma página inteira – valerá a pena?).

Código:

BITS 32

%define UNROLL_COUNT (4)

section .data

shuffle: dd 0x04050607, 0x00010203, 0x0c0d0e0f, 0x08090a0b

section .text
global _ssse3_bswap64
_ssse3_bswap64:
  push ebp
  mov edx, [esp+8] ; buffer -- assumed aligned 16
  mov ecx, [esp+12] ; length in #words
  test   ecx, ecx
  jz     _end

  and    ecx, -(UNROLL_COUNT*2) ; make ecx even
  jz     _finalize
  
  movdqa xmm7, [shuffle]

align 16
_loop:
  sub ecx, UNROLL_COUNT*2

  ; use movntdqa with sse 4.1   

  movdqa xmm0, [edx + 00]
  movdqa xmm1, [edx + 16]
  movdqa xmm2, [edx + 32]
  movdqa xmm3, [edx + 48]
  
  pshufb xmm0, xmm7 ; p5, 1l 1t
  pshufb xmm1, xmm7 ; p5, 1l 1t
  pshufb xmm2, xmm7 ; p5, 1l 1t
  pshufb xmm3, xmm7 ; p5, 1l 1t
  
  ; use movntdq --- the data won't be accessed again 
  ; until the end of the function
  movntdq [edx + 00], xmm0
  movntdq [edx + 16], xmm1
  movntdq [edx + 32], xmm2
  movntdq [edx + 48], xmm3

  lea edx, [edx+ 16*UNROLL_COUNT];
  jnz _loop ; no dependency, all flags were 
            ; computed in the beginning of the loop

_finalize:
  sfence ; serialize stores
  mov  ebp, [esp+8]
  and  ebp, (UNROLL_COUNT*2)-1 ; ebp = count mod UNROLL
  jz _end
  
_endloop:
  mov eax, [edx]
  bswap eax ; p0+p5
  mov ecx, [edx+4]
  bswap ecx
  mov [edx], ecx
  mov [edx+4], eax
  sub ebp, 1
  lea edx, [edx+8]
  jnz _endloop
  
_end:
  pop ebp
  ret

A performance agora é claramente superior em cerca de 10% — 1500 MB/s neste Core 2 a 2.0 GHz.

Adeus.

Abril 28, 2009

Byte Swap com SSE2

Arquivado em: assembly, coding, useless — dcoder @ 23:32

Encontrava-me hoje na Internet descansado quando o raxx7 começou a falar de optimizar uma função simples, mas engraçada – inverter a ordem dos bytes de um inteiro de 64 bits. Vou desde já assumir x86, visto que em amd64 existe uma instrução nativa que faz isto em apenas 4 ciclos (latência no core2) e é provavelmente rápido o suficiente. Mas em x86 precisamos de 2 instruções destas e 2+2 acessos à memória. Assim, peguei na identidade simples:

    BSWAP64(x) = BSWAP32(x>>32)|BSWAP32(x&0xFFFFFFFF)
    BSWAP32(x) = ((ROTL32((x), 8) & 0x00FF00FF) | (ROTL32((x), 24) & 0xFF00FF00))

,onde ROTL32 significa uma rotação de n bits à esquerda. Infelizmente, as extensões SSE2 da arquitectura x86 não possuem instruções específicas de rotação, forçando-nos a utilizar outra identidade bastante útil:

    ROTL32(x, n) = ((x< < n) | (x >> (32-n))) 

Neste momento, já temos tudo o que precisamos para efectuar a operação desejada em SSE2. No entanto, parece haver aqui um excesso de computação que torna este método demasiado ineficiente: 10 operações lógicas – 5 booleanas, 4 shifts e 1 shuffle. Mas a pipeline do Core 2 é extremamente boa: conseguimos efectuar 3 operações booleanas por ciclo, 1 shift por ciclo e 1 shuffle por ciclo. Se conseguirmos esconder a latência dos shifts com as operações booleanas, vamos obter uma contagem de ciclos não muito superior à dos 2 bswap de 32 bits. Além do mais, efectuamos 2 inversões simultâneas, dado que os registos XMM têm 128 bits; seria um desperdício não o fazer. Os acessos à memória também são mais eficientes, com acessos contíguos de 16 bytes (com o unrolling correcto processamos uma cache line (64 bytes) inteira de uma só vez).

Fiz então uma função para completar o exercício:

BITS 32

%define UNROLL_COUNT (4)

section .data
mask1: dd 0x00ff00ff, 0x00ff00ff, 0x00ff00ff, 0x00ff00ff
mask2: dd 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xff00ff00

section .text
global _sse2_bswap64
_sse2_bswap64:
  push ebp
  mov edx, [esp+8] ; buffer -- assumed aligned 16
  mov ecx, [esp+12] ; length in #words
  test   ecx, ecx
  jz     _end

  and    ecx, -(UNROLL_COUNT*2) ; make ecx even
  jz     _finalize
  
  movdqa xmm6, [mask1]
  movdqa xmm7, [mask2]

align 16
_loop:
  sub ecx, UNROLL_COUNT*2
  
%assign i 0  
%rep  UNROLL_COUNT
  
  movdqa xmm0, [edx] ; p2
  movdqa xmm1, xmm0  ; p0/p1/p5
  pslld  xmm0, 8     ; p0
  movdqa xmm2, xmm1  ; p0/p1/p5
  psrld  xmm1, 24;   ; p0
  movdqa xmm3, xmm2  ; p0/p1/p5
  pslld  xmm2, 24    ; p0
  por    xmm0, xmm1  ; p0/p1/p5
  psrld  xmm3, 8     ; p0
  por    xmm2, xmm3  ; p0/p1/p5
  pand   xmm0, xmm6  ; p0/p1/p5
  pand   xmm2, xmm7  ; p0/p1/p5
  por    xmm0, xmm2  ; p0/p1/p5
  pshufd xmm0, xmm0, 10110001b ; p5 + p0/p1
  movdqa [edx], xmm0 ; p2  
  lea    edx,  [edx+16] ; p0   
  
%assign i i + 1
%endrep

  jnz _loop ; no dependency, all flags were 
               ; computed in the beginning of the loop

_finalize:
  mov  ebp, [esp+8]
  and  ebp, (UNROLL_COUNT*2)-1 ; ebp = count mod UNROLL
  jz _end
  
_endloop:
  mov eax, [edx]
  bswap eax ; p0+p5
  mov ecx, [edx+4]
  bswap ecx
  mov [edx], ecx
  mov [edx+4], eax
  sub ebp, 1
  lea edx, [edx+8]
  jnz _endloop
  
_end:
  pop ebp
  ret

Este código pode ser usado com o yasm ou nasm (o yasm em win32 não acertava com os endereços das masks, não sei se era bug no exportador para COFF ou AIDS). Esta função, junto com código para testá-la encontra-se aqui.

A performance obtida não foi tão boa como esperado, mas mantém-se competitiva com a alternativa: no Core 2 (65 nm) onde testei, a versão SSE2 era ~2% mais rápida. A utilidade de todo este exercício é, assim, discutível.

Bem-haja.

Abril 26, 2009

Arvorezinha - Perl

Arquivado em: arvorezinha, coding, useless — devnull @ 16:07

Já que avançámos para linguagens mais normais e para demonstrar que o python suga, segue o meu one liner em perl.


david@tokyo ~ $ perl -l arvorezinha.pl
*
**
***
****
*****
david@tokyo ~ $ cat arvorezinha.pl
for (1..5){print "*"x$_;}
david@tokyo ~ $ wc -c arvorezinha.pl

O “-l” põe automaticamente o carriage return no fim de cada linha.

Bem-haja!

Abril 25, 2009

Arvorezinha em Brainfuck 2.0 - Com loops

Arquivado em: arvorezinha, coding, fail, useless — dcoder @ 21:42

Viva amigos.  Numa noite lenta como esta, decidi ceder aos pedidos de uma arvorezinha em brainfuck segundo as regras, isto é, sem ser hardcoded. E aqui está. Aviso desde já que podia ser reduzida pelo menos uns 20% com alguns melhoramentos mais ou menos óbvios, mas não tenho paciência para essas coisas. Podem alterar o número de iterações na quarta sequência de ‘+’.

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

Um grande bem-haja!

Abril 23, 2009

Arvorezinha .NET

Arquivado em: arvorezinha, coding, useless — spico @ 15:22

Não é ASP.NET .. Mas sim PAINT.NET

Dcoder e falso, fica ai o desafio para optimizarem esta arvorezinha em Paint.NET

arvorezinha

Abril 21, 2009

ArveZinHa em LOLCODE

Arquivado em: arvorezinha, coding, useless — drune @ 20:13

Olá a todos,

Estava eu também a acompanhar esta saga maravilhosa da criação da arvorezinha em tudo o que é possível e decidi dar o meu contributo na medida do possível.
Então decidi pegar numa linguagem bastante usada o LOLCODE e numa implementação ainda mais usada o LOLPYTHON e escrever uma arvorezinha nesta maravilhosa linguagem. Ora o interpretador desta coisa fantástica é apenas um ficheiro e serve para tudo e mais alguma coisa.

A linguagem que vos vou mostrar de seguida é perigosa e não deve ser tentada em casa. Além de ser necessário chafurdar no source code do interpretador, ainda é necessário muito tempo para saber o que é um CHEEZBURGER.

Então aqui teem a beldade. Divirtam-se:

I CAN HAZ CHEEZBURGER
L CAN HAS 6
Z CAN HAS CHEEZBURGER
K CAN HAZ ''
WHILE I CUTE?
  Z CAN HAZ Z ALONG WITH CHEEZBURGER
  IZ L BIG LIKE Z?
   K CAN HAZ K ALONG WITH '*'
   VISIBLE K
  NOPE?
   KTHXBYE

VISIBLE 'zZ (LOLArvezinha v0.1 - Dedicated to BLOL.ORG) zZ '

O resultado é qualquer coisa assim:

arvlol

Obrigado a todos

Arvorezinha dBASE

Arquivado em: arvorezinha, coding, useless — mirage @ 15:28

Olá caros jardineiros. Tenho assistido com agrado à proliferação de arvorezinhas (deveras ecológico), todavia não tenho tido disponibilidade para vos acompanhar, o que me tem entristecido. Não mais! Eis uma arvorezinha em dBASE (não confundir com clipper), a correr na versão IV, que tive o prazer de usar na escola secundária. Apraz-me o print implícito das variáveis, o que obrigou a modificar ligeiramente a lógica habitual das arvorezinhas. Aqui está um screenshot do dBASE IV para reavivar a memória:
dBASE IV
dBASE IV

O arvorezi.prg consiste apenas no seguinte:

a = "*"
do while len(a) < 5
a = a + "*"
enddo
wait

Eis o resultado:
Arvorezinha no dBASE IV
Arvorezinha no dBASE IV

Enquadra-se totalmente...

Arquivado em: coding, fail — madinfo @ 09:54

fail

Palavras para quê….

Abril 20, 2009

arvorezinha em x86 2.0 - boot sector

Arquivado em: arvorezinha, assembly, coding, fail — dcoder @ 10:09

Olá sirs,

Para completar a colecção fiz a arvorezinha como boot sector, sem recorrer a chamadas ao sistema ou à BIOS. Assim até podem usar uma BIOS opensource e a arvorezinha continua a funcionar!

Eis o código:

BITS 16
ORG 0x7C00

start:
mov ax, 0xb800
mov es, ax
mov ax, 0x072a
mov bx, 5
mov dx, 1
xor di, di
_loop1:
mov cx, dx
rep stosw
add di, 160
sub di, dx
sub di, dx
inc dx
cmp dx, bx
jbe _loop1

_loop2:
in al,0x60 ; read from keyboard
cmp al, 1 ; is it escape?
jnz _loop2

jmp 0xFFFF:0000 ; reboot ;-)

; pad to 512
times 510-($-$$) db 0
dw 0xAA55 ; magic boot value

Assemblem com o NASM, e guardem o output de 512 bytes no sector 0 de um disco ou disquete ou CD. As últimas instruções antes do padding servem para esperar que o utilizador carregue na tecla ESC; após isto, reinicia.

Output testada no VMWare:

boot