- 5 letters
- 4 letters
- 2 letters
- 1 letter
Pokazywanie postów oznaczonych etykietą c. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą c. Pokaż wszystkie posty
czwartek, 18 września 2014
String literals are weird (at least in C/C++)
Simple quiz: what is the length of this string "\xbadcafe"?
sobota, 22 marca 2014
C++ bitset vs array
C++ bitset conserves a memory, but at cost of speed access. Bitset must be slower than set represented as a plain old array, at least when sets are small (say few hundred elements).
Lets look at this simple functions:
The file was compiled with g++ -std=c++11 -O3 set_test.cpp; Assembly code of the core of any_in_byteset:
Now, look at assembly code of any_in_bitset:
All these instructions implements the if statement! Again we have load from memory (5f), but checking which bit is set require much more work. Input (edx) is split to lower part --- i.e. bit number (67, 6c) and higher part --- i.e. word index (6c). Last step is to check if a bit is set in a word --- GCC used variable shift left (6f), but x86 has BT instruction, so in a perfect code we would have 2 instructions less.
However, as we see simple access in the bitset is much more complicated than simple memory fetch from byteset. For short sets memory fetches are well cached and smaller number of instruction improves performance. For really large set cache misses would kill performance, then bitset is much better choice.
Lets look at this simple functions:
// set_test.cpp
#include <stdint.h>
#include <bitset>
const int size = 128;
typedef uint8_t byte_set[size];
bool any_in_byteset(uint8_t* data, size_t size, byte_set set) {
for (auto i=0u; i < size; i++)
if (set[data[i]])
return true;
return false;
}
typedef std::bitset<size> bit_set;
bool any_in_bitset(uint8_t* data, size_t size, bit_set set) {
for (auto i=0u; i < size; i++)
if (set[data[i]])
return true;
return false;
}
The file was compiled with g++ -std=c++11 -O3 set_test.cpp; Assembly code of the core of any_in_byteset:
28: 0f b6 10 movzbl (%eax),%edx 2b: 83 c0 01 add $0x1,%eax 2e: 80 3c 11 00 cmpb $0x0,(%ecx,%edx,1) 32: 75 0c jne 40 34: 39 d8 cmp %ebx,%eax 36: 75 f0 jne 28Statement if (set[data[i]]) return true are lines 28, 2e and 32, i.e.: load from memory, compare and jump. Instructions 2b, 34 and 36 handles the for loop.
Now, look at assembly code of any_in_bitset:
5f: 0f b6 13 movzbl (%ebx),%edx 62: b8 01 00 00 00 mov $0x1,%eax 67: 89 d1 mov %edx,%ecx 69: 83 e1 1f and $0x1f,%ecx 6c: c1 ea 05 shr $0x5,%edx 6f: d3 e0 shl %cl,%eax 71: 85 44 94 18 test %eax,0x18(%esp,%edx,4) 75: 75 39 jne b0
All these instructions implements the if statement! Again we have load from memory (5f), but checking which bit is set require much more work. Input (edx) is split to lower part --- i.e. bit number (67, 6c) and higher part --- i.e. word index (6c). Last step is to check if a bit is set in a word --- GCC used variable shift left (6f), but x86 has BT instruction, so in a perfect code we would have 2 instructions less.
However, as we see simple access in the bitset is much more complicated than simple memory fetch from byteset. For short sets memory fetches are well cached and smaller number of instruction improves performance. For really large set cache misses would kill performance, then bitset is much better choice.
środa, 19 marca 2014
Is const-correctness paranoia good?
Yes, definitely. Lets see this simple example:
$ cat test.cpp
int test(int x) {
if (x = 1)
return 42;
else
return 0;
}
$ g++ -c test.cpp
$ g++ -c -Wall test.cpp
int test(int x) {
if (x = 1)
return 42;
else
return 0;
}
Only when we turn on warnings, compiler tell us about a possible error.
Making the parameter const shows us error:
$ cat test2.cpp
int test(int x) {
if (x = 1)
return 42;
else
return 0;
}
$ g++ -c test.cpp
test2.cpp: In function ‘int test(int)’:
test2.cpp:2:8: error: assignment of read-only parameter ‘x’
if (x = 1)
^
All input parameters should be const, all write-once variables serving as a parameters for some computations should be also const.
poniedziałek, 3 marca 2014
Slow-paths in GNU libc strstr
I've observed that some patterns issued to strstr cause significant
slowdown.
Sample program kill-strstr.c executes strstr(data, pattern), where data is a large string (16MB) filled with character ?; patterns are read from command line.
On my machine following times were recorded:
Sample program kill-strstr.c executes strstr(data, pattern), where data is a large string (16MB) filled with character ?; patterns are read from command line.
On my machine following times were recorded:
1. searching string 'johndoe'...
time: 0.032
2. searching string '??????????????????a'...
time: 0.050
3. searching string '??????????????????????????????a'...
time: 0.049
4. searching string '???????????????????????????????a'...
time: 0.274
5. searching string '??????????????????????????????a?'...
time: 0.356
6. searching string '??????????????????????????????a??????????????????????????????'...
time: 0.396
- Slowdown is visible in case 4 (5 times slower than pattern 3). Pattern has 32 characters, and contains '?', except last char.
- Even bigger slowdown occurs in case 5 (7 times slower). This pattern also contains 32 chars, but position of the single letter 'a' is last but one.
- Similar slowdown occurs in case 5 (nearly 8 times slower). In this pattern single letter 'a' is surrounded by 30 '?'.
środa, 25 grudnia 2013
fopen a directory
It's not clear how function fopen applied to a directory should behave, manual pages don't say anything about this. So, our common sense fail --- at least when use standard library shipped with GCC, beacuse fopen returns a valid handle.
Discussion on stackoverflow pointed that fseek or ftell would fail. But on my system it's not true, ftell(f, 0, SEEK_END) returns size of opened directory.
Only when we trying to read data using fread or fgetc the errno variable is set to EISDIR error code.
Here is output from simple test program:
Discussion on stackoverflow pointed that fseek or ftell would fail. But on my system it's not true, ftell(f, 0, SEEK_END) returns size of opened directory.
Only when we trying to read data using fread or fgetc the errno variable is set to EISDIR error code.
Here is output from simple test program:
$ ./a.out ~ testing '/home/wojtek'... fopen: Success [errno=0] fseek: Success [errno=0] fseek result: 0 ftell: Success [errno=0] ftell result: 24576 feof: Success [errno=0] feof result: 0 (EOF=no) fgetc: Is a directory [errno=21] fgetc result: -1 (EOF=yes) fread: Is a directory [errno=21] fread result: 0
sobota, 9 kwietnia 2011
pyDAWG
pyDAWG is a python module implementing Directed Acyclic Word Graph, that allow to store set of words in a compacted way. DAWGs are much smaller then tries, while sharing the main advantage of tries - linear time to check if word is present in a set.
The main module is a C extension, there is also a pure python code.
The main module is a C extension, there is also a pure python code.
niedziela, 9 maja 2010
Branchless set mask if value greater or how to print hex values
Suppose we need to get mask when nonnegative argument is greater then some constant value; in other words, we want to evaluate following expression:
Portable branchless solution:
The key to understand this trick is binary form of M: 0111..1111zzzz, where z is 0 or 1 depending on n value. When x is greater then n, then x + M has form 1000..000zzzz, because carry bit propagate through series of ones to k-th position of result.
Real world example - branchless converting hex digit to ASCII (M=0x7ffffff6 for k=31 and n=9).
It is also possible to convert 4 hex digits in parallel using similar algorithm, but input data have to be correctly prepared. Moreover generating mask requires 3 instructon and one extra register (in scalar version just one arithmetic shift). I guess it wont be fast on x86, maybe this approach would be good for SIMD code, where similar code transforms more bytes at once.
See also: SSSE3: printing hex values (weird use of PSHUFB instruction)
if x > const_n then mask := 0xffffffff; else mask := 0x00000000;
Portable branchless solution:
- choose magic number M := (1 << (k-1)) - 1 - n, where k is a bit position, for example 31 if we operate on 32-bit words
- calculate R := x + M
- k-th bit of R is set if x > n
- fill mask with this bit - see note Fill word with selected bit
The key to understand this trick is binary form of M: 0111..1111zzzz, where z is 0 or 1 depending on n value. When x is greater then n, then x + M has form 1000..000zzzz, because carry bit propagate through series of ones to k-th position of result.
Real world example - branchless converting hex digit to ASCII (M=0x7ffffff6 for k=31 and n=9).
; input: eax - hex digit
; output: eax - ASCII letter (0-9, A-F or a-f)
; destroys: ebx
andl 0xf, %eax
leal 0x7ffffff6(%eax), %ebx ; MSB(ebx)=1 when eax >= 10
sarl $31, %ebx ; ebx - mask
andl $7, %ebx ; ebx = 7 when eax >= 10 (for A-F letters)
;andl $39, %ebx ; ebx = 39 when eax >= 10 (for a-f letters)
leal '0'(%eax, %ebx), %eax ; eax = '0' + eax + ebx => ASCII letter
It is also possible to convert 4 hex digits in parallel using similar algorithm, but input data have to be correctly prepared. Moreover generating mask requires 3 instructon and one extra register (in scalar version just one arithmetic shift). I guess it wont be fast on x86, maybe this approach would be good for SIMD code, where similar code transforms more bytes at once.
; input: eax - four hex digits in form [0a0b0c0d]
; output: eax - four ascii letters
; destroys: ebx, ecx
leal 0x76767676(%eax), %ebx ; MSB of each byte is set when corresponding eax byte is >= 10
; (here: 0x7f - 9 = 0x76)
andl $0x80808080, %ebx
movl %ebx, %ecx
shrl $7, %ebx
subl %ebx, %ecx ; ecx - byte-wise mask
;andl $0x07070707, %ecx ; for ASCII letters A-F
andl $0x27272727, %ecx ; for ASCII letters a-f
leal 0x30303030(%eax, %ecx), %eax ; ecx - four ascii letters
See also: SSSE3: printing hex values (weird use of PSHUFB instruction)
Subskrybuj:
Posty (Atom)