The MASM Forum

General => The Laboratory => Topic started by: Siekmanski on March 07, 2016, 07:41:33 AM

Title: Fast Bit Reversal algorithm
Post by: Siekmanski on March 07, 2016, 07:41:33 AM
    Bit-reversal is needed for a radix 2 FFT so, checked the output again.

    When I was looking for repeating patterns in my earlier bit-reversal routine, I noticed this:
    I could do bit-reversal without reversing bits.

8:   0  4  2  6                   |   1 5 3  7
16: 0  8  4 12 2 10  6 14  |   1 9 5 13 3 11 7 15
32: 0 16 8 24 4 20 12 28 etc. ( every next row is the next power of two )
   
    Second half = first half + 1 and my first thought was, cool, reduce size by 2 and add 1 to get the rest.
    To do it this way, you need a lot of memory read\writes so, not an option.
    Looked over it a few times and, EUREKA !

    I can create a 4*4 matrix to calculate 32 bit-reversals with 1 index ( 16 + 16+1 )
    by transposing 0 8 4 12 ( first quarter of length 16 ) to the first column.
    Then fill the rest of the columns for a 512 size ( 16 * 32 values from 16 indices )
    And it works, the matrix can be used to calculate all powers of two lengths and writes 4 values at once.
   
    Just by only adding the values with 1 and putting them at the right place via the matrix indices I can get all bit-reversals.   

    It's easy to create a new matrix from the BitreversalMatrix for all other power of two sizes.
    By multiplying ( power of two up ) or dividing the BitreversalMatrix ( power of two down ).
    Multiplying is of cource done by shifting ( because we are dealing with power of 2 calculations ).

    For example size 1024:
    Multiply the Matrix by 2 ( 1024 / 512 ) and add 4 for every next 4*4 Matrix step.
    For size 2048 then multiply the Matrix by 4 ( 2048 / 512 ) etc.
    Why dividing by 512 ?, because the BitreversalMatrix represents length 512.

    Next thing to do:
    Initialize the BitreversalMatrix for the size you need.
    Put the complete BitreversalMatrix into xmm4 - xmm7.
    Copy every next row to xmm3 then shuffle each next index to eax ( no memory reads anymore ).
    When you have done all 4 rows, add 4 for to every next 4*4 Matrix step.
    So you can keep all code in registers and use the complete data cache ( size is also a power of two ) to do fast bit-reversals.
    For example if you have a 32 KB L1 data cache, you can do a 8192 size bit-reversal very fast.

    I'll code this soon. Decided to post it at this stage because it is easier to explain the algorithm this way.
   
Some eaxamples to show the logic of creating the 4*4 matrix:

BitreversalMatrix   dd 00,32,16,48
                           dd 08,40,24,56
                           dd 04,36,20,52
                           dd 12,44,28,60

1024 dd 000, 064, 032, 096   ; First  row = BitreversalMatrix * 2 ( 1024 / 512 = 2 )
        dd 016, 080, 048, 112   ; Second row = BitreversalMatrix * 2
        dd 008, 072, 040, 104   ; Third  row = BitreversalMatrix * 2
        dd 024, 088, 056, 120   ; Fourth row = BitreversalMatrix * 2

        Next step refresh the Matrix with these new 16 values:

        dd 004, 068, 036, 100   ; First  row + 4
        dd 020, 084, 052, 116   ; Second row + 4
        dd 012, 076, 044, 108   ; Third  row + 4
        dd 028, 092, 060, 124   ; Fourth row + 4

512     ==  BitreversalMatrix

256     dd 00, 16, 08, 24       ; First  row = BitreversalMatrix / 2 ( 256 / 512 = 0.5 )
          dd 04, 20, 12, 28       ; Second row = BitreversalMatrix / 2

128     dd 00, 08, 04, 12       ; First  row = BitreversalMatrix / 4 ( 128 / 512 = 0.25 )

64       dd 00, 04, 02, 06       ; First  row = BitreversalMatrix / 8 (  64 / 512 = 0.125 )

32       dd 00, 02, 01, 03       ; First  row = BitreversalMatrix / 16 ( 32 / 512 = 0.0625 )

16  and 8 are less than 32 and are therefore special cases, but can be calculated like this,

    lea     esi,BitreversalMatrix
    lea     edi,BitreversalTable

    movdqa  xmm0,oword ptr [esi]
    movdqa  xmm1,oword ptr BitreversalADD

    cmp     BitreversalSize,16
    je      size_16
    cmp     BitreversalSize,8
    jne     done
    psrld   xmm0,3
    movdqa  oword ptr[edi],xmm0
    paddd   xmm0,xmm1
    movdqa  oword ptr[edi+BitreversalSize*2],xmm0
done:
    ret
size_16:
    psrld   xmm0,2
    movdqa  oword ptr[edi],xmm0
    paddd   xmm0,xmm1
    movdqa  oword ptr[edi+BitreversalSize*2],xmm0
    paddd   xmm0,xmm1
    movdqa  oword ptr[edi+BitreversalSize],xmm0
    paddd   xmm0,xmm1
    movdqa  oword ptr[edi+BitreversalSize*2+BitreversalSize],xmm0
    ret
Title: Re: Fast Bit Reversal algorithm
Post by: dedndave on March 09, 2016, 11:22:50 PM
you may find this post helpful Marinus

http://www.masmforum.com/board/index.php?topic=12722.msg98224#msg98224 (http://www.masmforum.com/board/index.php?topic=12722.msg98224#msg98224)
Title: Re: Fast Bit Reversal algorithm
Post by: Siekmanski on March 10, 2016, 12:05:31 AM
Hi Dave,

I'll compare the execution speed of those algorithms with mine this weekend.
Thanks for the link.
Title: Re: Fast Bit Reversal algorithm
Post by: dedndave on March 10, 2016, 04:19:38 AM
it wasn't my intention that you should use that code, directly
but, if you use that method with SSE code, it should be quite fast   :t
Title: Re: Fast Bit Reversal algorithm
Post by: Siekmanski on March 10, 2016, 05:36:25 AM
I'm always in the learning mood so, i'll study those examples.   :t
My version does 4 bit-reversals in one go. ( in fact 32 with 1 index pointer )
It doesn't do bit-reversal of single values but creates a table for a power of two size.
For example 128 values from 0 - 127 bit-reversed.
I could use a fast one to construct a time decomposition table for my FFT routine.
Title: Re: Fast Bit Reversal algorithm
Post by: Siekmanski on March 10, 2016, 08:59:39 AM
Hi Dave,

I have studied the bit reversal routines from the thread you posted.
I can not make a fair comparison because you all did a 32 bit bit-reversal of 1 value.

My routine does 128 values and writes them in a memory-buffer.
In my routine it doesn't matter how manny bits you need to reverse.
I still have to implement the Matrix preparation code for every bit-size.
No additional calculations are needed in the main loop, only for matrix shuffles and summations.

The routine in the attachment is fixed for a table of 128 values.
In this example for 128 values it reverses all 7 bits per dword, from value 0 --- > 127

I think it's rather fast including mem-writes.  :biggrin:

Timings on my machine:

Siekmanski bitswap: 82 cycles per 128 values = 0.640625 cycles per value
drizzNew stir-fry bitswap: 3 cycles per value ( the fastest from your posted thread )

Fast Bit-reversal using a 4*4 Matrix by Siekmanski 2016.

Please wait 4 seconds before timing begins

82 Cycles for 128 values.

Bitreversed Table results:
000 064 032 096 016 080 048 112 008 072 040 104 024 088 056 120 004 068 036 100
020 084 052 116 012 076 044 108 028 092 060 124 002 066 034 098 018 082 050 114
010 074 042 106 026 090 058 122 006 070 038 102 022 086 054 118 014 078 046 110
030 094 062 126 001 065 033 097 017 081 049 113 009 073 041 105 025 089 057 121
005 069 037 101 021 085 053 117 013 077 045 109 029 093 061 125 003 067 035 099
019 083 051 115 011 075 043 107 027 091 059 123 007 071 039 103 023 087 055 119
015 079 047 111 031 095 063 127

Press any key to continue...


Marinus
Title: Re: Fast Bit Reversal algorithm
Post by: Siekmanski on March 10, 2016, 09:50:38 AM
If have included "drizzNew stir-fry bitswap routine" to create a 128 size table.
Note: his routine includes memory writes now. ( for creating the table )

Fast Bit-reversal using a 4*4 Matrix by Siekmanski 2016.

81 Cycles Siekmanski
3585 Cycles drizzNew stir-fry

Siekmanski's table:
000 064 032 096 016 080 048 112 008 072 040 104 024 088 056 120 004 068 036 100
020 084 052 116 012 076 044 108 028 092 060 124 002 066 034 098 018 082 050 114
010 074 042 106 026 090 058 122 006 070 038 102 022 086 054 118 014 078 046 110
030 094 062 126 001 065 033 097 017 081 049 113 009 073 041 105 025 089 057 121
005 069 037 101 021 085 053 117 013 077 045 109 029 093 061 125 003 067 035 099
019 083 051 115 011 075 043 107 027 091 059 123 007 071 039 103 023 087 055 119
015 079 047 111 031 095 063 127

Drizznew's table:
000 064 032 096 016 080 048 112 008 072 040 104 024 088 056 120 004 068 036 100
020 084 052 116 012 076 044 108 028 092 060 124 002 066 034 098 018 082 050 114
010 074 042 106 026 090 058 122 006 070 038 102 022 086 054 118 014 078 046 110
030 094 062 126 001 065 033 097 017 081 049 113 009 073 041 105 025 089 057 121
005 069 037 101 021 085 053 117 013 077 045 109 029 093 061 125 003 067 035 099
019 083 051 115 011 075 043 107 027 091 059 123 007 071 039 103 023 087 055 119
015 079 047 111 031 095 063 127

Press any key to continue...
Title: Re: Fast Bit Reversal algorithm
Post by: Siekmanski on March 10, 2016, 07:33:03 PM
Hi ognil,
Replaced it with the faster drizzNew stir-fry  :t
It is still not a fair comparison, because drizznew's routine is optimised for one 32 bit reversal.

Fast Bit-reversal using a 4*4 Matrix by Siekmanski 2016.

76 Cycles Siekmanski
2765 Cycles drizzNew stir-fry

Siekmanski's table:
000 064 032 096 016 080 048 112 008 072 040 104 024 088 056 120 004 068 036 100
020 084 052 116 012 076 044 108 028 092 060 124 002 066 034 098 018 082 050 114
010 074 042 106 026 090 058 122 006 070 038 102 022 086 054 118 014 078 046 110
030 094 062 126 001 065 033 097 017 081 049 113 009 073 041 105 025 089 057 121
005 069 037 101 021 085 053 117 013 077 045 109 029 093 061 125 003 067 035 099
019 083 051 115 011 075 043 107 027 091 059 123 007 071 039 103 023 087 055 119
015 079 047 111 031 095 063 127

Drizznew's table:
000 064 032 096 016 080 048 112 008 072 040 104 024 088 056 120 004 068 036 100
020 084 052 116 012 076 044 108 028 092 060 124 002 066 034 098 018 082 050 114
010 074 042 106 026 090 058 122 006 070 038 102 022 086 054 118 014 078 046 110
030 094 062 126 001 065 033 097 017 081 049 113 009 073 041 105 025 089 057 121
005 069 037 101 021 085 053 117 013 077 045 109 029 093 061 125 003 067 035 099
019 083 051 115 011 075 043 107 027 091 059 123 007 071 039 103 023 087 055 119
015 079 047 111 031 095 063 127

Press any key to continue...
Title: Re: Fast Bit Reversal algorithm
Post by: dedndave on March 14, 2016, 04:24:50 AM
you missed my intention
examine the stir-fry code - see how it works
then, write SSE code that uses a similar technique   :P
Title: Re: Fast Bit Reversal algorithm
Post by: Siekmanski on March 14, 2016, 09:18:49 AM
Hi Dave,

I did understand your intention for me to have a look at Drizz's stir-fry method.  :t
I have studied it, and it can be done with that very clever technique from Drizz in SSE code to calculate 4 DWORD values in bit-reversed order at once.
For that case the technique of Drizz is unbeatable.

But, my goal was to get an array of all the values from 0 to 255 in a bit-reversed order ( or another power of two length array ).

I found a way to calculate a bit-reversed array without the need of bit mangling or bit manipulation.  :biggrin:

It skips all the calculations needed to reverse the bits of each value.
Because of skipping those calculations it has become a very fast routine which exactly fits my need of a fast calculated bit-reversed array.
In this case it is much faster then using Drizz's stir-fry method in SSE code.

The only math in my routine is, start with the first values from the matrix, then increase those values with 1 to get the next values and place them at the right position in the array and repeat this untill all 256 values are done.
It only needs 256/32 steps to get all the 256 DWORD values in bit-reversed order.
With 1 index pointer from the matrix it puts 8 * 4 values at once at the right position in the array.

Note: This algorithm only works for power of two length arrays !
Title: Re: Fast Bit Reversal algorithm
Post by: Raistlin on March 14, 2016, 04:45:02 PM
Note: This algorithm only works for power of two length arrays !

Which makes perfect sense as data runs are typically 512, 1024, 2048, 4096 and at the extreme 32768 in length
per FFT sub-set. (I have seen 65536 and larger runs in hardware based systems) As far as I understand it the bit-reversal is
calculated once and only once. Then dumped as a lookup table. The data is then dispersed per this lookup-table. The result is fed into
the actual FFT algorithm. Normally a filter or transform is then applied. The result is then reversed (iFFT) and a new
waveform generated that thus incorporates such filters. Power of two use; as per Siekmanski seems intuitive and well conceived.
Please correct me, if I left something major out -  I'am only a novice at this myself.

refs see:
http://www.fftw.org/accuracy/Pentium4-3.60GHz-icc/ (Shows typical data bin sizes for powers of two)
http://www.originlab.com/doc/Origin-Help/FFT-Filter (Filters - general)
http://retouchpro.com/tutorials/?m=show&id=185 (Filters - photographic enhancement)
http://stackoverflow.com/questions/2929401/dsp-filtering-in-the-frequency-domain-via-fft  (Filters - audio)
http://www3.mpifr-bonn.mpg.de/staff/bklein/FFTS/URSI-FFTS.pdf  (hardware based FFT)
Title: Re: Fast Bit Reversal algorithm
Post by: hutch-- on March 14, 2016, 05:56:32 PM
i don't claim to have absorbed all of the code present but a 256 item table is one idea I would explore. It means for any give 8 bits you have a 1 memory access to get its inversion. Does this make sense ?
Title: Re: Fast Bit Reversal algorithm
Post by: K_F on March 14, 2016, 09:53:11 PM
Sorry Hutch, I meant the duplicate entry... I had two duplicate posts appear ?
Anyway back to topic..

If you're creating a single filter to work on many times a single bit reversal would do.
But if you're creating many filters (say a running filter) you have the problem of having to bit reverse every filter.

I've got around this by making an offset address table with 2ExpN sizes and bit reversed this table.
The filter calculations then use this table as an index into the untouched/original filter for it's operations.
On changing the filter, you can just reload a saved filter or make a new one without having to bit reverse anything.

This is what I done so far.. I'll clean it up later.
:)
Title: Re: Fast Bit Reversal algorithm
Post by: Siekmanski on March 15, 2016, 12:20:36 AM
Hi guys,

We had this discussion before in the fft thread, do we realy need a fast bit-reversal routine for the fft routine ?
Not realy if you are repeating the same "small" fft length over and over again.
But if you need very large fft's it can take some time to create a bit-reversal table.
Especially if you need many fft's of differrent sizes.

My routine doesn't need a lookup table or do actual bit-reversal for any power of 2 size.
In fact it only needs these 4 numbers ( 00, 02, 01, 03 ) to create a matrix on the fly in code for all power of 2 sizes.
It shifts the 4 numbers into the right values for the size you want and transposes the first row to the first collumn and calculates the stride in that column needed for the places in the table.

The only thing it has to do is setting up the matrix values for the power of 2 size you want.
Then it processes the first row of the matrix then the second row and then updates the first and second row to row 3 and 4 if larger sizes are needed etc.

As soon as I have the time to code it, I will post a complete routine which does it all.
Just posted it at this stage to explain how the algoritm works with some fixed examples.

This technique ( with a bit of tweaking ) can be used to create the cos and sin tables.
To create a cos table and a sin table for a 1024 size fft, you only need to calculate 255 sines to construct both the cos and sin tables.
Title: Re: Fast Bit Reversal algorithm
Post by: K_F on March 15, 2016, 03:01:41 AM
I'm always curious... when you've finished  :biggrin:
Title: Re: Fast Bit Reversal algorithm
Post by: Siekmanski on March 26, 2016, 08:26:45 PM
I finished a routine that calculates 16 to 1024 size tables.
The algorithm works but I don't have enough registers to calculate larger then 1024 size tables. ( at least I think so.... )
I may need to store temporary pre calculated column values inside the table memory to calculate larger power of 2 size tables.

After all the goal is, to use this algorithm to calculate very large tables as fast as possible.

Edit: see Reply #26 for the source code
Title: Re: Fast Bit Reversal algorithm
Post by: nidud on March 26, 2016, 11:04:57 PM
deleted
Title: Re: Fast Bit Reversal algorithm
Post by: jj2007 on March 27, 2016, 06:34:16 AM
Works fine for me on Win7-64, even if launched directly from the archive.
Title: Re: Fast Bit Reversal algorithm
Post by: Siekmanski on March 27, 2016, 03:04:30 PM
Hi Nidud,
Can't find what could cause this....
Title: Re: Fast Bit Reversal algorithm
Post by: jj2007 on March 27, 2016, 07:05:29 PM
Could be unaligned access. @Nidud: If you configure Olly as just-in-time debugger, where does it crash?7
->Options/debugging/just-in-time
Title: Re: Fast Bit Reversal algorithm
Post by: Siekmanski on March 27, 2016, 08:39:55 PM
Memory access is 16 byte aligned.
Title: Re: Fast Bit Reversal algorithm
Post by: jj2007 on March 27, 2016, 09:03:07 PM
In fact, I can't produce a crash, whatever I try ::)
Title: Re: Fast Bit Reversal algorithm
Post by: nidud on March 27, 2016, 10:16:05 PM
deleted
Title: Re: Fast Bit Reversal algorithm
Post by: jj2007 on March 27, 2016, 10:46:01 PM
Quote from: nidud on March 27, 2016, 10:16:05 PMIt could be a hardware problem. The CPU is <= SSE3.

movdqa is SSE2 (https://msdn.microsoft.com/en-us/library/bytwczae%28v=vs.90%29.aspx). Besides, Olly is powerful but it can't upgrade the hardware :icon_mrgreen:
Title: Re: Fast Bit Reversal algorithm
Post by: nidud on March 27, 2016, 11:42:46 PM
deleted
Title: Re: Fast Bit Reversal algorithm
Post by: nidud on March 27, 2016, 11:46:13 PM
deleted
Title: Re: Fast Bit Reversal algorithm
Post by: Siekmanski on March 28, 2016, 02:26:01 AM
Nidud.

Thanks, for finding this bug, xmm4 is indeed undefined when it jumps to NextColumn.  :t
Title: Re: Fast Bit Reversal algorithm
Post by: jj2007 on May 28, 2016, 07:40:57 PM
Here is a slow but easy-to-use MirrorBits() macro ruthlessly stolen from The Aggregate at University of Kentucky (http://aggregate.org/MAGIC/#Bit%20Reversal) :biggrin:

include \masm32\MasmBasic\MasmBasic.inc

MirrorBits MACRO arg32
  ifdifi <arg32>, <eax>
mov eax, arg32
  endif
  mov edx, eax
  and eax, 0aaaaaaaah
  and edx, 055555555h
  shr eax, 1
  add edx, edx ; shl edx,1
  or eax, edx
  mov edx, eax
  and eax, 0cccccccch
  and edx, 033333333h
  shr eax, 2
  lea edx, [4*edx] ; shl edx, 2
  or eax, edx
  mov edx, eax
  and eax, 0f0f0f0f0h
  and edx, 0f0f0f0fh
  shr eax, 4
  shl edx, 4
  or eax, edx
  mov edx, eax
  and eax, 0ff00ff00h
  and edx, 0ff00ffh
  shr eax, 8
  shl edx, 8
  or eax, edx
  mov edx, eax
  shr eax, 16
  shl edx, 16
  or eax, edx
  EXITM <eax>
ENDM

  Init
  push 29
  .Repeat
mov ecx, Rand(-1)
Print Bin$(ecx), " "
Print Bin$(MirrorBits(ecx)), CrLf$
dec stack
  .Until Sign?
EndOfCode


01110011101010011011100010001101 10110001000111011001010111001110
00010010011100011100110110011101 10111001101100111000111001001000
00100010111111101000101011011000 00011011010100010111111101000100
11011101000111011011100110100010 01000101100111011011100010111011
10100111111001110100000001001101 10110010000000101110011111100101
11010010000110000100011110110011 11001101111000100001100001001011
00111000011011011111010011000100 00100011001011111011011000011100
11001001100100010111001100000000 00000000110011101000100110010011
00000101111100101101011011111111 11111111011010110100111110100000
10000100010101101000011100001011 11010000111000010110101000100001
10010000000000000001001101011000 00011010110010000000000000001001
01011101011001101111011010001110 01110001011011110110011010111010
10010011101011101110101110101000 00010101110101110111010111001001
10101101000101100011100110111011 11011101100111000110100010110101
01000000010110111111100100100000 00000100100111111101101000000010
00100101011010000101111100010001 10001000111110100001011010100100
10010110110101010011100001110000 00001110000111001010101101101001
01110101111111110011100000101011 11010100000111001111111110101110
10110000001000010110001101110101 10101110110001101000010000001101
11111010101001110110111110011110 01111001111101101110010101011111
10100011100111111110111110100101 10100101111101111111100111000101
00101010000111001010100011010010 01001011000101010011100001010100
11010111001111101100100000011010 01011000000100110111110011101011
00011111101110101101101000110110 01101100010110110101110111111000
00111011101111100100110101000001 10000010101100100111110111011100
11000110101010111001011101011011 11011010111010011101010101100011
11100000111010001101111101101111 11110110111110110001011100000111
11110100001001100111100101101011 11010110100111100110010000101111
11110000110000101110100111011111 11111011100101110100001100001111
01100100111110010100110110001110 01110001101100101001111100100110