निम्नलिखित प्राप्त करने के लिए सबसे कुशल एल्गोरिथ्म क्या है:
0010 0000 => 0000 0100
रूपांतरण MSB-> LSB से LSB-> MSB है। सभी बिट्स को उलट करना चाहिए; यह है कि, यह है नहीं endianness-स्वैपिंग।
निम्नलिखित प्राप्त करने के लिए सबसे कुशल एल्गोरिथ्म क्या है:
0010 0000 => 0000 0100
रूपांतरण MSB-> LSB से LSB-> MSB है। सभी बिट्स को उलट करना चाहिए; यह है कि, यह है नहीं endianness-स्वैपिंग।
जवाबों:
ध्यान दें : सभी एल्गोरिदम नीचे सी में हैं, लेकिन आपकी पसंद की भाषा के लिए पोर्टेबल होना चाहिए (बस मुझे मत देखो जब वे उतने तेज़ नहीं होते हैं :)
कम मेमोरी (32-बिट int
, 32-बिट मशीन) ( यहाँ से ):
unsigned int
reverse(register unsigned int x)
{
x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
return((x >> 16) | (x << 16));
}
प्रसिद्ध बिट ट्विडलिंग हैक्स पेज से :
सबसे तेज़ (लुकअप टेबल) :
static const unsigned char BitReverseTable256[] =
{
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
unsigned int v; // reverse 32-bit value, 8 bits at time
unsigned int c; // c will get v reversed
// Option 1:
c = (BitReverseTable256[v & 0xff] << 24) |
(BitReverseTable256[(v >> 8) & 0xff] << 16) |
(BitReverseTable256[(v >> 16) & 0xff] << 8) |
(BitReverseTable256[(v >> 24) & 0xff]);
// Option 2:
unsigned char * p = (unsigned char *) &v;
unsigned char * q = (unsigned char *) &c;
q[3] = BitReverseTable256[p[0]];
q[2] = BitReverseTable256[p[1]];
q[1] = BitReverseTable256[p[2]];
q[0] = BitReverseTable256[p[3]];
आप इस विचार को 64-बिट int
एस तक बढ़ा सकते हैं , या गति के लिए मेमोरी को व्यापार कर सकते हैं (यह मानकर कि आपका L1 डेटा कैश काफी बड़ा है), और एक 64K- एंट्री लुकअप टेबल के साथ एक बार में 16 बिट्स रिवर्स करें।
सरल
unsigned int v; // input bits to be reversed
unsigned int r = v & 1; // r will be reversed bits of v; first get LSB of v
int s = sizeof(v) * CHAR_BIT - 1; // extra shift needed at end
for (v >>= 1; v; v >>= 1)
{
r <<= 1;
r |= v & 1;
s--;
}
r <<= s; // shift when v's highest bits are zero
तेज़ (32-बिट प्रोसेसर)
unsigned char b = x;
b = ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16;
तेज़ (64-बिट प्रोसेसर)
unsigned char b; // reverse this (8-bit) byte
b = (b * 0x0202020202ULL & 0x010884422010ULL) % 1023;
यदि आप इसे 32-बिट पर करना चाहते हैं int
, तो बस प्रत्येक बाइट में बिट्स को उल्टा करें, और बाइट्स के क्रम को उल्टा करें। अर्थात्:
unsigned int toReverse;
unsigned int reversed;
unsigned char inByte0 = (toReverse & 0xFF);
unsigned char inByte1 = (toReverse & 0xFF00) >> 8;
unsigned char inByte2 = (toReverse & 0xFF0000) >> 16;
unsigned char inByte3 = (toReverse & 0xFF000000) >> 24;
reversed = (reverseBits(inByte0) << 24) | (reverseBits(inByte1) << 16) | (reverseBits(inByte2) << 8) | (reverseBits(inByte3);
मैंने दो सबसे आशाजनक समाधान, लुकअप टेबल, और बिटवाइज़-और (पहला वाला) बेंचमार्क किया। परीक्षण मशीन एक लैपटॉप w / 4GB का DDR2-800 और एक कोर 2 डुओ T7500 @ 2.4GHz, 4MB L2 कैश; YMMV। मैंने जीसीसी का इस्तेमाल किया 64-बिट लिनक्स पर 4.3.2 । OpenMP (और GCC बाइंडिंग) का उपयोग उच्च-रिज़ॉल्यूशन टाइमर के लिए किया गया था।
reverse.c
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>
unsigned int
reverse(register unsigned int x)
{
x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
return((x >> 16) | (x << 16));
}
int main()
{
unsigned int *ints = malloc(100000000*sizeof(unsigned int));
unsigned int *ints2 = malloc(100000000*sizeof(unsigned int));
for(unsigned int i = 0; i < 100000000; i++)
ints[i] = rand();
unsigned int *inptr = ints;
unsigned int *outptr = ints2;
unsigned int *endptr = ints + 100000000;
// Starting the time measurement
double start = omp_get_wtime();
// Computations to be measured
while(inptr != endptr)
{
(*outptr) = reverse(*inptr);
inptr++;
outptr++;
}
// Measuring the elapsed time
double end = omp_get_wtime();
// Time calculation (in seconds)
printf("Time: %f seconds\n", end-start);
free(ints);
free(ints2);
return 0;
}
reverse_lookup.c
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>
static const unsigned char BitReverseTable256[] =
{
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
int main()
{
unsigned int *ints = malloc(100000000*sizeof(unsigned int));
unsigned int *ints2 = malloc(100000000*sizeof(unsigned int));
for(unsigned int i = 0; i < 100000000; i++)
ints[i] = rand();
unsigned int *inptr = ints;
unsigned int *outptr = ints2;
unsigned int *endptr = ints + 100000000;
// Starting the time measurement
double start = omp_get_wtime();
// Computations to be measured
while(inptr != endptr)
{
unsigned int in = *inptr;
// Option 1:
//*outptr = (BitReverseTable256[in & 0xff] << 24) |
// (BitReverseTable256[(in >> 8) & 0xff] << 16) |
// (BitReverseTable256[(in >> 16) & 0xff] << 8) |
// (BitReverseTable256[(in >> 24) & 0xff]);
// Option 2:
unsigned char * p = (unsigned char *) &(*inptr);
unsigned char * q = (unsigned char *) &(*outptr);
q[3] = BitReverseTable256[p[0]];
q[2] = BitReverseTable256[p[1]];
q[1] = BitReverseTable256[p[2]];
q[0] = BitReverseTable256[p[3]];
inptr++;
outptr++;
}
// Measuring the elapsed time
double end = omp_get_wtime();
// Time calculation (in seconds)
printf("Time: %f seconds\n", end-start);
free(ints);
free(ints2);
return 0;
}
मैंने कई अलग-अलग अनुकूलन पर दोनों दृष्टिकोणों की कोशिश की, प्रत्येक स्तर पर 3 परीक्षण किए, और प्रत्येक परीक्षण 100 मिलियन यादृच्छिक रूप से उलट हुआ unsigned ints
। लुकअप टेबल विकल्प के लिए, मैंने बिटवाइज हैक पेज पर दी गई दोनों योजनाओं (विकल्प 1 और 2) की कोशिश की। परिणाम नीचे दिखाए गए हैं।
बिटवाइज़ और
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -o reverse reverse.c
mrj10@mjlap:~/code$ ./reverse
Time: 2.000593 seconds
mrj10@mjlap:~/code$ ./reverse
Time: 1.938893 seconds
mrj10@mjlap:~/code$ ./reverse
Time: 1.936365 seconds
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -O2 -o reverse reverse.c
mrj10@mjlap:~/code$ ./reverse
Time: 0.942709 seconds
mrj10@mjlap:~/code$ ./reverse
Time: 0.991104 seconds
mrj10@mjlap:~/code$ ./reverse
Time: 0.947203 seconds
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -O3 -o reverse reverse.c
mrj10@mjlap:~/code$ ./reverse
Time: 0.922639 seconds
mrj10@mjlap:~/code$ ./reverse
Time: 0.892372 seconds
mrj10@mjlap:~/code$ ./reverse
Time: 0.891688 seconds
लुकअप तालिका (विकल्प 1)
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -o reverse_lookup reverse_lookup.c
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.201127 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.196129 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.235972 seconds
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -O2 -o reverse_lookup reverse_lookup.c
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 0.633042 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 0.655880 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 0.633390 seconds
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -O3 -o reverse_lookup reverse_lookup.c
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 0.652322 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 0.631739 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 0.652431 seconds
लुकअप तालिका (विकल्प 2)
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -o reverse_lookup reverse_lookup.c
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.671537 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.688173 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.664662 seconds
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -O2 -o reverse_lookup reverse_lookup.c
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.049851 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.048403 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.085086 seconds
mrj10@mjlap:~/code$ gcc -fopenmp -std=c99 -O3 -o reverse_lookup reverse_lookup.c
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.082223 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.053431 seconds
mrj10@mjlap:~/code$ ./reverse_lookup
Time: 1.081224 seconds
विकल्प 1 के साथ, लुकअप तालिका का उपयोग करेंयदि आप प्रदर्शन के बारे में चिंतित हैं, तो (बाइट एड्रेसिंग बहुत धीमी है)। यदि आपको अपने सिस्टम से मेमोरी के प्रत्येक अंतिम बाइट को निचोड़ने की ज़रूरत है (और यदि आप बिट रिवर्सल के प्रदर्शन की परवाह करते हैं), तो बिटवाइज़-एंड दृष्टिकोण के अनुकूलित संस्करण बहुत जर्जर नहीं हैं।
हां, मुझे पता है कि बेंचमार्क कोड एक पूर्ण हैक है। स्वागत से अधिक यह कैसे सुधारने के सुझाव हैं। जिन चीजों के बारे में मुझे पता है:
ld
कुछ पागल प्रतीक पुनर्वितरण त्रुटि के साथ उड़ा दिया), इसलिए मुझे विश्वास नहीं है कि उत्पन्न कोड मेरे माइक्रोआर्किटेक्चर के लिए ट्यून किया गया है।32-बिट
.L3:
movl (%r12,%rsi), %ecx
movzbl %cl, %eax
movzbl BitReverseTable256(%rax), %edx
movl %ecx, %eax
shrl $24, %eax
mov %eax, %eax
movzbl BitReverseTable256(%rax), %eax
sall $24, %edx
orl %eax, %edx
movzbl %ch, %eax
shrl $16, %ecx
movzbl BitReverseTable256(%rax), %eax
movzbl %cl, %ecx
sall $16, %eax
orl %eax, %edx
movzbl BitReverseTable256(%rcx), %eax
sall $8, %eax
orl %eax, %edx
movl %edx, (%r13,%rsi)
addq $4, %rsi
cmpq $400000000, %rsi
jne .L3
संपादित करें: मैंने यह uint64_t
देखने के लिए कि क्या कोई प्रदर्शन को बढ़ावा देने के लिए अपनी मशीन पर प्रकारों का उपयोग करने की कोशिश की है । प्रदर्शन 32-बिट की तुलना में लगभग 10% तेज था, और लगभग समान था कि क्या आप int
एक समय में दो 32-बिट प्रकारों पर 64-बिट प्रकारों को रिवर्स बिट्स का उपयोग कर रहे थे या क्या आप वास्तव में बिट्स को आधे में उलट रहे थे जैसे कि 64- थोड़ा मान। असेंबली कोड नीचे दिखाया गया है (पूर्व मामले के लिए, int
एक समय में दो 32-बिट प्रकारों के लिए बिट्स को उल्टा करना):
.L3:
movq (%r12,%rsi), %rdx
movq %rdx, %rax
shrq $24, %rax
andl $255, %eax
movzbl BitReverseTable256(%rax), %ecx
movzbq %dl,%rax
movzbl BitReverseTable256(%rax), %eax
salq $24, %rax
orq %rax, %rcx
movq %rdx, %rax
shrq $56, %rax
movzbl BitReverseTable256(%rax), %eax
salq $32, %rax
orq %rax, %rcx
movzbl %dh, %eax
shrq $16, %rdx
movzbl BitReverseTable256(%rax), %eax
salq $16, %rax
orq %rax, %rcx
movzbq %dl,%rax
shrq $16, %rdx
movzbl BitReverseTable256(%rax), %eax
salq $8, %rax
orq %rax, %rcx
movzbq %dl,%rax
shrq $8, %rdx
movzbl BitReverseTable256(%rax), %eax
salq $56, %rax
orq %rax, %rcx
movzbq %dl,%rax
shrq $8, %rdx
movzbl BitReverseTable256(%rax), %eax
andl $255, %edx
salq $48, %rax
orq %rax, %rcx
movzbl BitReverseTable256(%rdx), %eax
salq $40, %rax
orq %rax, %rcx
movq %rcx, (%r13,%rsi)
addq $8, %rsi
cmpq $400000000, %rsi
jne .L3
इस धागे ने मेरा ध्यान आकर्षित किया क्योंकि यह एक साधारण समस्या से निपटता है जिसमें आधुनिक सीपीयू के लिए भी बहुत सारे काम (सीपीयू चक्र) की आवश्यकता होती है। और एक दिन मैं भी उसी ¤ #% "#" समस्या के साथ वहां खड़ा था। मुझे लाखों बाइट पलटने थे। हालाँकि मुझे पता है कि मेरी सभी लक्षित प्रणालियाँ आधुनिक इंटेल आधारित हैं इसलिए चलो चरम पर अनुकूलन शुरू करते हैं !!!
इसलिए मैंने बेस के रूप में मैट जे के लुकअप कोड का उपयोग किया। मैं जिस सिस्टम पर बेंचमार्किंग कर रहा हूं वह एक i7 हैशवेल 4700eq है।
मैट जे की लुकिंग बिटफ्लिंग 400 000 000 बाइट्स: लगभग 0.272 सेकंड।
मैंने तब आगे बढ़कर यह देखने की कोशिश की कि क्या Intel का ISPC कंपाइलर उल्टे हाथ में अंकगणित को वेक्टर कर सकता है।
मैं यहाँ अपने निष्कर्षों से आपको बोर नहीं करने जा रहा हूँ क्योंकि मैंने कंपाइलर को सामान खोजने में मदद करने के लिए बहुत कोशिश की, किसी भी तरह मैं 400 000 000 बाइट्स को बिटफ्लिप करने के लिए लगभग 0.15 सेकंड के प्रदर्शन के साथ समाप्त हो गया। यह एक बड़ी कमी है लेकिन मेरे आवेदन के लिए अभी भी रास्ता बहुत धीमा है।
इसलिए लोग मुझे दुनिया में सबसे तेज इंटेल आधारित बिटफ्लियर पेश करने देते हैं। पर देखा:
400000000 बाइट्स बिटफ्लिप करने का समय: 0.050082 सेकंड !!!!!
// Bitflip using AVX2 - The fastest Intel based bitflip in the world!!
// Made by Anders Cedronius 2014 (anders.cedronius (you know what) gmail.com)
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <omp.h>
using namespace std;
#define DISPLAY_HEIGHT 4
#define DISPLAY_WIDTH 32
#define NUM_DATA_BYTES 400000000
// Constants (first we got the mask, then the high order nibble look up table and last we got the low order nibble lookup table)
__attribute__ ((aligned(32))) static unsigned char k1[32*3]={
0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,0x0f,
0x00,0x08,0x04,0x0c,0x02,0x0a,0x06,0x0e,0x01,0x09,0x05,0x0d,0x03,0x0b,0x07,0x0f,0x00,0x08,0x04,0x0c,0x02,0x0a,0x06,0x0e,0x01,0x09,0x05,0x0d,0x03,0x0b,0x07,0x0f,
0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0,0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0
};
// The data to be bitflipped (+32 to avoid the quantization out of memory problem)
__attribute__ ((aligned(32))) static unsigned char data[NUM_DATA_BYTES+32]={};
extern "C" {
void bitflipbyte(unsigned char[],unsigned int,unsigned char[]);
}
int main()
{
for(unsigned int i = 0; i < NUM_DATA_BYTES; i++)
{
data[i] = rand();
}
printf ("\r\nData in(start):\r\n");
for (unsigned int j = 0; j < 4; j++)
{
for (unsigned int i = 0; i < DISPLAY_WIDTH; i++)
{
printf ("0x%02x,",data[i+(j*DISPLAY_WIDTH)]);
}
printf ("\r\n");
}
printf ("\r\nNumber of 32-byte chunks to convert: %d\r\n",(unsigned int)ceil(NUM_DATA_BYTES/32.0));
double start_time = omp_get_wtime();
bitflipbyte(data,(unsigned int)ceil(NUM_DATA_BYTES/32.0),k1);
double end_time = omp_get_wtime();
printf ("\r\nData out:\r\n");
for (unsigned int j = 0; j < 4; j++)
{
for (unsigned int i = 0; i < DISPLAY_WIDTH; i++)
{
printf ("0x%02x,",data[i+(j*DISPLAY_WIDTH)]);
}
printf ("\r\n");
}
printf("\r\n\r\nTime to bitflip %d bytes: %f seconds\r\n\r\n",NUM_DATA_BYTES, end_time-start_time);
// return with no errors
return 0;
}
प्रिंटफ की डिबगिंग के लिए हैं ।।
ये है वर्कहॉर्स:
bits 64
global bitflipbyte
bitflipbyte:
vmovdqa ymm2, [rdx]
add rdx, 20h
vmovdqa ymm3, [rdx]
add rdx, 20h
vmovdqa ymm4, [rdx]
bitflipp_loop:
vmovdqa ymm0, [rdi]
vpand ymm1, ymm2, ymm0
vpandn ymm0, ymm2, ymm0
vpsrld ymm0, ymm0, 4h
vpshufb ymm1, ymm4, ymm1
vpshufb ymm0, ymm3, ymm0
vpor ymm0, ymm0, ymm1
vmovdqa [rdi], ymm0
add rdi, 20h
dec rsi
jnz bitflipp_loop
ret
कोड 32 बाइट्स लेता है फिर निबल्स को मास्क करता है। उच्च कुतरना 4. द्वारा सही स्थानांतरित हो जाता है फिर मैं लुकअप तालिकाओं के रूप में vpshufb और ymm4 / ymm3 का उपयोग करता हूं। मैं एक भी लुकअप टेबल का उपयोग कर सकता था, लेकिन फिर मुझे एक बार फिर से निबल्स को ऑर्ड करने से पहले बाईं ओर शिफ्ट करना होगा।
बिट्स को फ़्लिप करने के और भी तेज़ तरीके हैं। लेकिन मैं सिंगल थ्रेड और सीपीयू के लिए बाध्य हूं इसलिए यह सबसे तेज था जिसे मैं हासिल कर सकता था। क्या आप एक तेज़ संस्करण बना सकते हैं?
कृपया इंटेल C / C ++ कंपाइलर आंतरिक समतुल्य कमांड का उपयोग करने के बारे में कोई टिप्पणी न करें ...
pshub
, क्योंकि इसके बाद सबसे अच्छा पॉपकॉइन भी किया जाता है! मैं इसे यहाँ लिखा होता अगर आपके लिए नहीं होता। कुडोस।
popcnt
, tzcnt
और pext
हर इसलिए सभी बंदरगाह 1. पर pext
या tzcnt
आप लागत एक popcnt
प्रवाह की। यदि आपका डेटा L1D कैश में गर्म है, तो इंटेल सीपीयू पर एक सरणी को पॉपकॉउट करने का सबसे तेज़ तरीका AVX2 pufufb के साथ है। (राइज़ेन में प्रति घड़ी 4 popcnt
थ्रूपुट है, इसलिए शायद यह इष्टतम है, लेकिन बुलडोज़र-परिवार में प्रति 4 क्लॉकpopcnt r64,r64
थ्रूपुट में से एक है ... agner.org/optimize )।
यह उन लोगों के लिए एक और समाधान है जो पुनरावृत्ति से प्यार करते हैं।
विचार सरल है। इनपुट को आधे से विभाजित करें और दो हिस्सों को स्वैप करें, तब तक जारी रखें जब तक कि यह एकल बिट तक न पहुंच जाए।
Illustrated in the example below.
Ex : If Input is 00101010 ==> Expected output is 01010100
1. Divide the input into 2 halves
0010 --- 1010
2. Swap the 2 Halves
1010 0010
3. Repeat the same for each half.
10 -- 10 --- 00 -- 10
10 10 10 00
1-0 -- 1-0 --- 1-0 -- 0-0
0 1 0 1 0 1 0 0
Done! Output is 01010100
इसे हल करने के लिए एक पुनरावर्ती कार्य है। (नोट मैंने अहस्ताक्षरित ints का उपयोग किया है, इसलिए यह आकार के लिए इनपुट के लिए काम कर सकता है (अहस्ताक्षरित int) * 8 बिट्स।
पुनरावर्ती फ़ंक्शन 2 पैरामीटर लेता है - वह मान जिसके बिट्स को उलटने की आवश्यकता होती है और मूल्य में बिट्स की संख्या।
int reverse_bits_recursive(unsigned int num, unsigned int numBits)
{
unsigned int reversedNum;;
unsigned int mask = 0;
mask = (0x1 << (numBits/2)) - 1;
if (numBits == 1) return num;
reversedNum = reverse_bits_recursive(num >> numBits/2, numBits/2) |
reverse_bits_recursive((num & mask), numBits/2) << numBits/2;
return reversedNum;
}
int main()
{
unsigned int reversedNum;
unsigned int num;
num = 0x55;
reversedNum = reverse_bits_recursive(num, 8);
printf ("Bit Reversal Input = 0x%x Output = 0x%x\n", num, reversedNum);
num = 0xabcd;
reversedNum = reverse_bits_recursive(num, 16);
printf ("Bit Reversal Input = 0x%x Output = 0x%x\n", num, reversedNum);
num = 0x123456;
reversedNum = reverse_bits_recursive(num, 24);
printf ("Bit Reversal Input = 0x%x Output = 0x%x\n", num, reversedNum);
num = 0x11223344;
reversedNum = reverse_bits_recursive(num,32);
printf ("Bit Reversal Input = 0x%x Output = 0x%x\n", num, reversedNum);
}
यह आउटपुट है:
Bit Reversal Input = 0x55 Output = 0xaa
Bit Reversal Input = 0xabcd Output = 0xb3d5
Bit Reversal Input = 0x123456 Output = 0x651690
Bit Reversal Input = 0x11223344 Output = 0x22cc4488
numBits
कि int है, जब आप फ़ंक्शन परम के लिए 3 को 2 से विभाजित करते हैं तो इसे 1 से नीचे किया जाएगा?
खैर यह निश्चित रूप से मैट जे की तरह एक जवाब नहीं होगा, लेकिन उम्मीद है कि यह अभी भी उपयोगी होगा।
size_t reverse(size_t n, unsigned int bytes)
{
__asm__("BSWAP %0" : "=r"(n) : "0"(n));
n >>= ((sizeof(size_t) - bytes) * 8);
n = ((n & 0xaaaaaaaaaaaaaaaa) >> 1) | ((n & 0x5555555555555555) << 1);
n = ((n & 0xcccccccccccccccc) >> 2) | ((n & 0x3333333333333333) << 2);
n = ((n & 0xf0f0f0f0f0f0f0f0) >> 4) | ((n & 0x0f0f0f0f0f0f0f0f) << 4);
return n;
}
यह बिल्कुल मैट के सर्वश्रेष्ठ एल्गोरिदम के समान विचार है, सिवाय इसके कि बीएसडब्ल्यूएपी नामक यह छोटा निर्देश है जो 64-बिट संख्या के बाइट्स (बिट्स नहीं) को स्वैप करता है। तो बी 7, बी 6, बी 5, बी 4, बी 3, बी 2, बी 1, बी 0 बी 0, बी 1, बी 2, बी 3, बी 4, बी 5, बी 6, बी 7 बन जाता है। चूंकि हम 32-बिट संख्या के साथ काम कर रहे हैं, इसलिए हमें अपनी बाइट-स्वैप की गई संख्या को 32 बिट्स पर शिफ्ट करने की आवश्यकता है। यह बस हमें प्रत्येक बाइट के 8 बिट्स को स्वैप करने के कार्य के साथ छोड़ देता है जो कि किया जाता है और वॉइला! हमारा काम हो गया।
टाइमिंग: मेरी मशीन पर, मैट का एल्गोरिदम प्रति परीक्षण ~ 0.52 सेकंड में चला। मेरा प्रति परीक्षण लगभग 0.42 सेकंड में चला। 20% तेजी से बुरा नहीं है मुझे लगता है।
यदि आप निर्देश की उपलब्धता के बारे में चिंतित हैं तो BSWAP विकिपीडिया निर्देश BSWAP को 80846 के साथ जोड़े जाने के रूप में सूचीबद्ध करता है जो 1989 में सामने आया था। यह ध्यान दिया जाना चाहिए कि विकिपीडिया यह भी बताता है कि यह निर्देश केवल 32 बिट रजिस्टरों पर काम करता है जो स्पष्ट रूप से नहीं है मेरी मशीन पर मामला, यह बहुत ही 64-बिट रजिस्टरों पर काम करता है।
यह विधि किसी भी इंटीग्रल डेटाटाइप के लिए समान रूप से अच्छी तरह से काम करेगी ताकि बाइट्स की संख्या को पास करके विधि को सामान्य रूप से सामान्यीकृत किया जा सके:
size_t reverse(size_t n, unsigned int bytes)
{
__asm__("BSWAP %0" : "=r"(n) : "0"(n));
n >>= ((sizeof(size_t) - bytes) * 8);
n = ((n & 0xaaaaaaaaaaaaaaaa) >> 1) | ((n & 0x5555555555555555) << 1);
n = ((n & 0xcccccccccccccccc) >> 2) | ((n & 0x3333333333333333) << 2);
n = ((n & 0xf0f0f0f0f0f0f0f0) >> 4) | ((n & 0x0f0f0f0f0f0f0f0f) << 4);
return n;
}
जिसे तब इस तरह बुलाया जा सकता है:
n = reverse(n, sizeof(char));//only reverse 8 bits
n = reverse(n, sizeof(short));//reverse 16 bits
n = reverse(n, sizeof(int));//reverse 32 bits
n = reverse(n, sizeof(size_t));//reverse 64 bits
कंपाइलर अतिरिक्त पैरामीटर को अनुकूलित करने में सक्षम होना चाहिए (कंपाइलर फ़ंक्शन को मानता है) और इस sizeof(size_t)
मामले के लिए सही-शिफ्ट पूरी तरह से हटा दिया जाएगा। ध्यान दें कि GCC कम से कम BSWAP को हटाने और पास होने पर राइट-शिफ्ट करने में सक्षम नहीं है sizeof(char)
।
एंडर्स सेड्रोनियस का जवाब उन लोगों के लिए एक शानदार समाधान प्रदान करता है जिनके पास एवीएक्स 2 समर्थन वाला एक x86 सीपीयू है। एवीएक्स समर्थन या गैर-एक्स 86 प्लेटफार्मों के बिना x86 प्लेटफार्मों के लिए, निम्नलिखित कार्यान्वयन में से किसी एक को अच्छी तरह से काम करना चाहिए।
पहला कोड क्लासिक बाइनरी विभाजन विधि का एक प्रकार है, जिसे विभिन्न एआरएम प्रोसेसर पर उपयोगी शिफ्ट-प्लस-लॉजिक मुहावर के उपयोग को अधिकतम करने के लिए कोडित किया गया है। इसके अलावा, यह ऑन-द-फ्लाई मास्क पीढ़ी का उपयोग करता है जो RISC प्रोसेसर के लिए फायदेमंद हो सकता है, अन्यथा प्रत्येक 32-बिट मास्क मूल्य को लोड करने के लिए कई निर्देशों की आवश्यकता होती है। X86 प्लेटफ़ॉर्म के कंपाइलरों को रन टाइम के बजाय सभी मास्क को कंपाइल टाइम पर कंपोज़ करने के लिए लगातार प्रोपेगेशन का इस्तेमाल करना चाहिए।
/* Classic binary partitioning algorithm */
inline uint32_t brev_classic (uint32_t a)
{
uint32_t m;
a = (a >> 16) | (a << 16); // swap halfwords
m = 0x00ff00ff; a = ((a >> 8) & m) | ((a << 8) & ~m); // swap bytes
m = m^(m << 4); a = ((a >> 4) & m) | ((a << 4) & ~m); // swap nibbles
m = m^(m << 2); a = ((a >> 2) & m) | ((a << 2) & ~m);
m = m^(m << 1); a = ((a >> 1) & m) | ((a << 1) & ~m);
return a;
}
"द आर्ट ऑफ़ कंप्यूटर प्रोग्रामिंग" के वॉल्यूम 4 ए में, डी। नथ ने बिट्स को उलटने के चतुर तरीके दिखाए हैं जो कुछ हद तक आश्चर्यजनक रूप से शास्त्रीय बाइनरी विभाजन एल्गोरिदम की तुलना में कम संचालन की आवश्यकता होती है। 32-बिट ऑपरेंड्स के लिए एक ऐसा एल्गोरिथ्म, जो मुझे TAOCP में नहीं मिल रहा है, हैकर की डिलाइट वेबसाइट पर इस दस्तावेज़ में दिखाया गया है ।
/* Knuth's algorithm from http://www.hackersdelight.org/revisions.pdf. Retrieved 8/19/2015 */
inline uint32_t brev_knuth (uint32_t a)
{
uint32_t t;
a = (a << 15) | (a >> 17);
t = (a ^ (a >> 10)) & 0x003f801f;
a = (t + (t << 10)) ^ a;
t = (a ^ (a >> 4)) & 0x0e038421;
a = (t + (t << 4)) ^ a;
t = (a ^ (a >> 2)) & 0x22488842;
a = (t + (t << 2)) ^ a;
return a;
}
Intel कंपाइलर C / C ++ कंपाइलर 13.1.3.198 का उपयोग करते हुए, उपरोक्त दोनों फ़ंक्शन ऑटो-वेक्टराइज़ करते हैं अच्छी तरह से टारगेटिंग XMM
रजिस्टर। उन्हें बहुत प्रयास के बिना मैन्युअल रूप से वेक्टर किया जा सकता है।
मेरे IvyBridge Xeon E3 1270v2 पर, ऑटो-वेक्टर कोड का उपयोग करते हुए, 100 मिलियन uint32_t
शब्दों का उपयोग करते हुए 0.070 सेकंड में बिट-रिवर्स किया गया था brev_classic()
, और 0.068 सेकंड का उपयोग किया गया था brev_knuth()
। मैंने यह सुनिश्चित करने के लिए ध्यान रखा कि मेरा बेंचमार्क सिस्टम मेमोरी बैंडविड्थ द्वारा सीमित नहीं था।
brev_knuth()
? हैकर की डिलाइट से पीडीएफ में एट्रिब्यूशन से संकेत मिलता है कि ये नंबर सीधे नूथ से हैं। मैं यह दावा करने का दावा नहीं कर सकता कि टीएओसीपी में अंतर्निहित डिजाइन सिद्धांतों के बारे में नुट को यह बताने के लिए पर्याप्त रूप से समझाया गया है कि स्थिरांक कैसे निकाले गए थे, या कैसे व्युत्पन्न स्थिरांक के बारे में जाना जाएगा और मनमाने शब्द आकारों के लिए कारकों को स्थानांतरित किया जाएगा।
यह मानते हुए कि आपके पास बिट्स की एक सरणी है, इस बारे में कैसे: 1. एमएसबी से शुरू करके, बिट्स को एक एक करके एक स्टैक में धकेल दें। 2. इस स्टैक से पॉप बिट्स को किसी अन्य एरे में (या उसी एरे को यदि आप स्पेस बचाना चाहते हैं), पहले पॉपप्ड बिट को MSB में रखें और वहां से कम महत्वपूर्ण बिट्स पर जाएं।
Stack stack = new Stack();
Bit[] bits = new Bit[] { 0, 0, 1, 0, 0, 0, 0, 0 };
for (int i = 0; i < bits.Length; i++)
{
stack.push(bits[i]);
}
for (int i = 0; i < bits.Length; i++)
{
bits[i] = stack.pop();
}
यह एक मानव के लिए कोई काम नहीं है! ... लेकिन एक मशीन के लिए एकदम सही
यह 2015 है, 6 साल जब से यह सवाल पहली बार पूछा गया था। कंपाइलर तब से हमारे स्वामी बन गए हैं, और मनुष्य के रूप में हमारा काम केवल उनकी मदद करना है। तो मशीन को हमारे इरादे देने का सबसे अच्छा तरीका क्या है?
बिट-उलट इतना आम है कि आपको आश्चर्य होगा कि क्यों x86 की बढ़ती आईएसए में इसे एक बार करने का निर्देश शामिल नहीं है।
कारण: यदि आप संकलक को अपना सच्चा संक्षिप्त इरादा देते हैं, तो थोड़ा उलट केवल ~ 20 सीपीयू चक्र लेना चाहिए । मुझे दिखाते हैं कि कैसे शिल्प को उल्टा करना है () और इसका उपयोग करें:
#include <inttypes.h>
#include <stdio.h>
uint64_t reverse(const uint64_t n,
const uint64_t k)
{
uint64_t r, i;
for (r = 0, i = 0; i < k; ++i)
r |= ((n >> i) & 1) << (k - i - 1);
return r;
}
int main()
{
const uint64_t size = 64;
uint64_t sum = 0;
uint64_t a;
for (a = 0; a < (uint64_t)1 << 30; ++a)
sum += reverse(a, size);
printf("%" PRIu64 "\n", sum);
return 0;
}
इस नमूने कार्यक्रम को क्लैंग संस्करण के साथ संकलित करना = = 3.6; -ओ 3, -मेरच = देशी (हसवेल के साथ परीक्षण), नए एवीएक्स 2 निर्देशों का उपयोग करके कलाकृति-गुणवत्ता कोड देता है, जिसमें 11 सेकंड का एक रन प्रोसेसिंग ~ 1 बिलियन रिवर्स () होता है। यह ~ 10 ns प्रति रिवर्स (), .5 ns CPU चक्र के साथ 2 GHz मानकर हमें मीठे 20 CPU चक्रों में डालता है।
कैविएट: यह नमूना कोड कुछ वर्षों के लिए एक अच्छे बेंचमार्क के रूप में होना चाहिए, लेकिन यह अंततः अपनी उम्र को दिखाना शुरू कर देगा क्योंकि कंपाइलर्स मुख्य () को अनुकूलित करने के लिए स्मार्ट हैं ताकि वास्तव में किसी भी चीज की गणना करने के बजाय अंतिम परिणाम प्रिंट किया जा सके। लेकिन अभी के लिए यह रिवर्स दिखाने () में काम करता है।
Bit-reversal is so common...
मैं उस बारे में नहीं जानता। मैं कोड के साथ काम करता हूं जो लगभग हर दिन बिट स्तर पर डेटा के साथ काम करता है, और मुझे याद नहीं हो सकता है कि कभी भी इसकी विशिष्ट आवश्यकता थी। आपको किन परिदृश्यों में इसकी आवश्यकता है? - ऐसा नहीं है कि यह अपने आप में हल करने के लिए एक दिलचस्प समस्या नहीं है।
निश्चित रूप से बिट-ट्विडलिंग हैक्स का स्पष्ट स्रोत यहां है: http://graphics.stanford.edu/~seander/bithacks.html#BitReverseOb जाहिर
मुझे पता है कि यह सी नहीं है लेकिन एएसएम:
var1 dw 0f0f0
clc
push ax
push cx
mov cx 16
loop1:
shl var1
shr ax
loop loop1
pop ax
pop cx
यह कैरी बिट के साथ काम करता है, इसलिए आप झंडे भी बचा सकते हैं
rcl
सीएफ को स्थानांतरित करना चाहते हैं var1
, इसके बजाय केवल shl
झंडे नहीं पढ़ते हैं। (या adc dx,dx
) उस फिक्स के साथ भी, यह धीमी गति से loop
निर्देश का उपयोग करके और var1
स्मृति में रखते हुए , हास्यास्पद रूप से धीमा है ! वास्तव में मुझे लगता है कि यह AX में आउटपुट का उत्पादन करने वाला है, लेकिन यह परिणाम के शीर्ष पर AX के पुराने मान को बचाता / पुनर्स्थापित करता है।
खैर, यह मूल रूप से पहले "रिवर्स" () के रूप में ही है, लेकिन यह 64 बिट है और केवल एक तत्काल मास्क को निर्देश धारा से लोड करने की आवश्यकता है। जीसीसी बिना जंप के कोड बनाता है, इसलिए यह बहुत तेज होना चाहिए।
#include <stdio.h>
static unsigned long long swap64(unsigned long long val)
{
#define ZZZZ(x,s,m) (((x) >>(s)) & (m)) | (((x) & (m))<<(s));
/* val = (((val) >>16) & 0xFFFF0000FFFF) | (((val) & 0xFFFF0000FFFF)<<16); */
val = ZZZZ(val,32, 0x00000000FFFFFFFFull );
val = ZZZZ(val,16, 0x0000FFFF0000FFFFull );
val = ZZZZ(val,8, 0x00FF00FF00FF00FFull );
val = ZZZZ(val,4, 0x0F0F0F0F0F0F0F0Full );
val = ZZZZ(val,2, 0x3333333333333333ull );
val = ZZZZ(val,1, 0x5555555555555555ull );
return val;
#undef ZZZZ
}
int main(void)
{
unsigned long long val, aaaa[16] =
{ 0xfedcba9876543210,0xedcba9876543210f,0xdcba9876543210fe,0xcba9876543210fed
, 0xba9876543210fedc,0xa9876543210fedcb,0x9876543210fedcba,0x876543210fedcba9
, 0x76543210fedcba98,0x6543210fedcba987,0x543210fedcba9876,0x43210fedcba98765
, 0x3210fedcba987654,0x210fedcba9876543,0x10fedcba98765432,0x0fedcba987654321
};
unsigned iii;
for (iii=0; iii < 16; iii++) {
val = swap64 (aaaa[iii]);
printf("A[]=%016llX Sw=%016llx\n", aaaa[iii], val);
}
return 0;
}
मैं उत्सुक था कि स्पष्ट कच्चा घुमाव कितनी तेजी से होगा। मेरी मशीन (i7 @ 2600) पर, 1,500,150,000 पुनरावृत्तियों के लिए औसत 27.28 ns
(131,071 64-बिट पूर्णांकों के यादृच्छिक क्रम पर) था।
लाभ: आवश्यक मेमोरी की मात्रा कम है और कोड सरल है। मैं कहूंगा कि यह उतना बड़ा नहीं है। आवश्यक समय किसी भी इनपुट के लिए अनुमानित और स्थिर है (128 अंकगणितीय SHIFT संचालन + 64 तार्किक और संचालन + 64 तार्किक या संचालन)।
मैंने @M J के द्वारा प्राप्त किए गए सर्वश्रेष्ठ समय की तुलना में - जिसके पास स्वीकृत उत्तर है। अगर मैं उनके उत्तर को सही ढंग से पढ़ता हूं, तो उन्हें जो सबसे अच्छा मिला है वह पुनरावृत्तियों के 0.631739
लिए सेकंड था 1,000,000
, जो औसत 631 ns
प्रति रोटेशन की ओर जाता है ।
कोड स्निपेट जिसका मैंने उपयोग किया है, यह नीचे दिया गया है:
unsigned long long reverse_long(unsigned long long x)
{
return (((x >> 0) & 1) << 63) |
(((x >> 1) & 1) << 62) |
(((x >> 2) & 1) << 61) |
(((x >> 3) & 1) << 60) |
(((x >> 4) & 1) << 59) |
(((x >> 5) & 1) << 58) |
(((x >> 6) & 1) << 57) |
(((x >> 7) & 1) << 56) |
(((x >> 8) & 1) << 55) |
(((x >> 9) & 1) << 54) |
(((x >> 10) & 1) << 53) |
(((x >> 11) & 1) << 52) |
(((x >> 12) & 1) << 51) |
(((x >> 13) & 1) << 50) |
(((x >> 14) & 1) << 49) |
(((x >> 15) & 1) << 48) |
(((x >> 16) & 1) << 47) |
(((x >> 17) & 1) << 46) |
(((x >> 18) & 1) << 45) |
(((x >> 19) & 1) << 44) |
(((x >> 20) & 1) << 43) |
(((x >> 21) & 1) << 42) |
(((x >> 22) & 1) << 41) |
(((x >> 23) & 1) << 40) |
(((x >> 24) & 1) << 39) |
(((x >> 25) & 1) << 38) |
(((x >> 26) & 1) << 37) |
(((x >> 27) & 1) << 36) |
(((x >> 28) & 1) << 35) |
(((x >> 29) & 1) << 34) |
(((x >> 30) & 1) << 33) |
(((x >> 31) & 1) << 32) |
(((x >> 32) & 1) << 31) |
(((x >> 33) & 1) << 30) |
(((x >> 34) & 1) << 29) |
(((x >> 35) & 1) << 28) |
(((x >> 36) & 1) << 27) |
(((x >> 37) & 1) << 26) |
(((x >> 38) & 1) << 25) |
(((x >> 39) & 1) << 24) |
(((x >> 40) & 1) << 23) |
(((x >> 41) & 1) << 22) |
(((x >> 42) & 1) << 21) |
(((x >> 43) & 1) << 20) |
(((x >> 44) & 1) << 19) |
(((x >> 45) & 1) << 18) |
(((x >> 46) & 1) << 17) |
(((x >> 47) & 1) << 16) |
(((x >> 48) & 1) << 15) |
(((x >> 49) & 1) << 14) |
(((x >> 50) & 1) << 13) |
(((x >> 51) & 1) << 12) |
(((x >> 52) & 1) << 11) |
(((x >> 53) & 1) << 10) |
(((x >> 54) & 1) << 9) |
(((x >> 55) & 1) << 8) |
(((x >> 56) & 1) << 7) |
(((x >> 57) & 1) << 6) |
(((x >> 58) & 1) << 5) |
(((x >> 59) & 1) << 4) |
(((x >> 60) & 1) << 3) |
(((x >> 61) & 1) << 2) |
(((x >> 62) & 1) << 1) |
(((x >> 63) & 1) << 0);
}
आप मानक टेम्पलेट लाइब्रेरी का उपयोग करना चाह सकते हैं। यह उपर्युक्त कोड की तुलना में धीमा हो सकता है। हालांकि, यह मुझे स्पष्ट और समझने में आसान लगता है।
#include<bitset>
#include<iostream>
template<size_t N>
const std::bitset<N> reverse(const std::bitset<N>& ordered)
{
std::bitset<N> reversed;
for(size_t i = 0, j = N - 1; i < N; ++i, --j)
reversed[j] = ordered[i];
return reversed;
};
// test the function
int main()
{
unsigned long num;
const size_t N = sizeof(num)*8;
std::cin >> num;
std::cout << std::showbase << std::hex;
std::cout << "ordered = " << num << std::endl;
std::cout << "reversed = " << reverse<N>(num).to_ulong() << std::endl;
std::cout << "double_reversed = " << reverse<N>(reverse<N>(num)).to_ulong() << std::endl;
}
सामान्य
सी कोड। उदाहरण के लिए 1 बाइट इनपुट डेटा संख्या का उपयोग करना।
unsigned char num = 0xaa; // 1010 1010 (aa) -> 0101 0101 (55)
int s = sizeof(num) * 8; // get number of bits
int i, x, y, p;
int var = 0; // make var data type to be equal or larger than num
for (i = 0; i < (s / 2); i++) {
// extract bit on the left, from MSB
p = s - i - 1;
x = num & (1 << p);
x = x >> p;
printf("x: %d\n", x);
// extract bit on the right, from LSB
y = num & (1 << i);
y = y >> i;
printf("y: %d\n", y);
var = var | (x << i); // apply x
var = var | (y << p); // apply y
}
printf("new: 0x%x\n", new);
निम्नलिखित के बारे में कैसे:
uint reverseMSBToLSB32ui(uint input)
{
uint output = 0x00000000;
uint toANDVar = 0;
int places = 0;
for (int i = 1; i < 32; i++)
{
places = (32 - i);
toANDVar = (uint)(1 << places);
output |= (uint)(input & (toANDVar)) >> places;
}
return output;
}
छोटा और आसान (हालाँकि, केवल 32 बिट)।
मैंने सोचा कि यह बिट को उल्टा करने का सबसे सरल तरीका है। कृपया मुझे बताएं कि क्या इस तर्क में कोई दोष है। मूल रूप से इस तर्क में, हम स्थिति में बिट के मूल्य की जांच करते हैं। यदि सेट उलटा स्थिति पर 1 है तो बिट सेट करें।
void bit_reverse(ui32 *data)
{
ui32 temp = 0;
ui32 i, bit_len;
{
for(i = 0, bit_len = 31; i <= bit_len; i++)
{
temp |= (*data & 1 << i)? (1 << bit_len-i) : 0;
}
*data = temp;
}
return;
}
unsigned char ReverseBits(unsigned char data)
{
unsigned char k = 0, rev = 0;
unsigned char n = data;
while(n)
{
k = n & (~(n - 1));
n &= (n - 1);
rev |= (128 / k);
}
return rev;
}
k
हमेशा 2 की शक्ति होती है, लेकिन कंपाइलर शायद यह साबित नहीं करेंगे और इसे बिट-स्कैन / शिफ्ट में बदल देंगे।
मुझे लगता है कि सबसे आसान तरीका मुझे पता है। MSB
इनपुट है और LSB
'उलटा' आउटपुट है:
unsigned char rev(char MSB) {
unsigned char LSB=0; // for output
_FOR(i,0,8) {
LSB= LSB << 1;
if(MSB&1) LSB = LSB | 1;
MSB= MSB >> 1;
}
return LSB;
}
// It works by rotating bytes in opposite directions.
// Just repeat for each byte.
// Purpose: to reverse bits in an unsigned short integer
// Input: an unsigned short integer whose bits are to be reversed
// Output: an unsigned short integer with the reversed bits of the input one
unsigned short ReverseBits( unsigned short a )
{
// declare and initialize number of bits in the unsigned short integer
const char num_bits = sizeof(a) * CHAR_BIT;
// declare and initialize bitset representation of integer a
bitset<num_bits> bitset_a(a);
// declare and initialize bitset representation of integer b (0000000000000000)
bitset<num_bits> bitset_b(0);
// declare and initialize bitset representation of mask (0000000000000001)
bitset<num_bits> mask(1);
for ( char i = 0; i < num_bits; ++i )
{
bitset_b = (bitset_b << 1) | bitset_a & mask;
bitset_a >>= 1;
}
return (unsigned short) bitset_b.to_ulong();
}
void PrintBits( unsigned short a )
{
// declare and initialize bitset representation of a
bitset<sizeof(a) * CHAR_BIT> bitset(a);
// print out bits
cout << bitset << endl;
}
// Testing the functionality of the code
int main ()
{
unsigned short a = 17, b;
cout << "Original: ";
PrintBits(a);
b = ReverseBits( a );
cout << "Reversed: ";
PrintBits(b);
}
// Output:
Original: 0000000000010001
Reversed: 1000100000000000
एक और लूप-आधारित समाधान जो संख्या कम होने पर जल्दी से बाहर निकलता है (कई प्रकारों के लिए C ++ में)
template<class T>
T reverse_bits(T in) {
T bit = static_cast<T>(1) << (sizeof(T) * 8 - 1);
T out;
for (out = 0; bit && in; bit >>= 1, in >>= 1) {
if (in & 1) {
out |= bit;
}
}
return out;
}
या एक अहस्ताक्षरित int के लिए C में
unsigned int reverse_bits(unsigned int in) {
unsigned int bit = 1u << (sizeof(T) * 8 - 1);
unsigned int out;
for (out = 0; bit && in; bit >>= 1, in >>= 1) {
if (in & 1)
out |= bit;
}
return out;
}
ऐसा लगता है कि कई अन्य पोस्ट गति के बारे में चिंतित हैं (यानी सबसे अच्छा = सबसे तेज़)। सादगी का क्या? विचार करें:
char ReverseBits(char character) {
char reversed_character = 0;
for (int i = 0; i < 8; i++) {
char ith_bit = (c >> i) & 1;
reversed_character |= (ith_bit << (sizeof(char) - 1 - i));
}
return reversed_character;
}
और आशा है कि चतुर कंपाइलर आपके लिए अनुकूलन करेगा।
यदि आप बिट्स ( sizeof(char) * n
बिट्स युक्त ) की लंबी सूची को उलटना चाहते हैं , तो आप इस फ़ंक्शन का उपयोग कर सकते हैं:
void ReverseNumber(char* number, int bit_count_in_number) {
int bytes_occupied = bit_count_in_number / sizeof(char);
// first reverse bytes
for (int i = 0; i <= (bytes_occupied / 2); i++) {
swap(long_number[i], long_number[n - i]);
}
// then reverse bits of each individual byte
for (int i = 0; i < bytes_occupied; i++) {
long_number[i] = ReverseBits(long_number[i]);
}
}
यह [10000000, 10101010] को [01010101, 00000001] में उलट देगा।
ith_bit = (c >> i) & 1
। reversed_char
बिट को शिफ्ट करने के बजाए शिफ्ट करके एक SUB को सेव करें , जब तक कि आप यह उम्मीद नहीं कर रहे हैं कि यह गंतव्य रजिस्टर में nth बिट सेट करने के लिए x86 sub something
/ bts reg,reg
to पर संकलित होगा ।
छद्म कोड में बिट उत्क्रमण
स्रोत -> बाइट को b00101100 गंतव्य से उलट किया जाना है -> उलट, भी अहस्ताक्षरित प्रकार का होना चाहिए ताकि साइन बिट का प्रचार न हो।
अस्थायी मूल में प्रतिलिपि अप्रभावित है, को भी अहस्ताक्षरित प्रकार का होना चाहिए ताकि साइन बिट स्वचालित रूप से शिफ्ट न हो जाए
bytecopy = b0010110
LOOP8: // यह 8 बार परीक्षण करें यदि बायटेकॉपी <0 (नकारात्मक) है
set bit8 (msb) of reversed = reversed | b10000000
else do not set bit8
shift bytecopy left 1 place
bytecopy = bytecopy << 1 = b0101100 result
shift result right 1 place
reversed = reversed >> 1 = b00000000
8 times no then up^ LOOP8
8 times yes then done.
मेरा सरल उपाय
BitReverse(IN)
OUT = 0x00;
R = 1; // Right mask ...0000.0001
L = 0; // Left mask 1000.0000...
L = ~0;
L = ~(i >> 1);
int size = sizeof(IN) * 4; // bit size
while(size--){
if(IN & L) OUT = OUT | R; // start from MSB 1000.xxxx
if(IN & R) OUT = OUT | L; // start from LSB xxxx.0001
L = L >> 1;
R = R << 1;
}
return OUT;
i
? इसके अलावा, वह जादू क्या है * 4
? क्या यह है CHAR_BIT / 2
?
यह 32 बिट के लिए है, हमें 8 बिट्स पर विचार करने पर आकार बदलने की आवश्यकता है।
void bitReverse(int num)
{
int num_reverse = 0;
int size = (sizeof(int)*8) -1;
int i=0,j=0;
for(i=0,j=size;i<=size,j>=0;i++,j--)
{
if((num >> i)&1)
{
num_reverse = (num_reverse | (1<<j));
}
}
printf("\n rev num = %d\n",num_reverse);
}
LSB-> MSB ऑर्डर में इनपुट पूर्णांक "संख्या" पढ़ना और MSB-> LSB ऑर्डर में num_reverse में स्टोर करना।
int bit_reverse(int w, int bits)
{
int r = 0;
for (int i = 0; i < bits; i++)
{
int bit = (w & (1 << i)) >> i;
r |= bit << (bits - i - 1);
}
return r;
}