I am working with a game state which is received as a string and needs to be converted into a BitBoard. I believe I have a function that accomplishes that now, but want to know how I can optimize it for faster execution? I originally started with a loop with the following idea:
for (i = 0; i < 23; ++i)
{
if (s.at(n) == 'x') set bit[2], // Start with x as there are more x's
else if(s.at(n) == 'W') set bit[0], // W next as W goes first
else set bit[1] // B last
}
but figured that I could just unroll the loop and skip the comparison and incrementation of 'i'. After doing that, I figured I could remove that last check for 'B', and just take the compliment of W | x and subtract 4286578688 from it to give me just 23 bits. That gave me the following code:
std::string board = "xBWxWxBWxWxxBxxxWxBxxBx"; // String to convert to bitboard
unsigned int bit; // Integer used for parsing values
unsigned int boards[3] {0, 0, 0}; // W in [0], B in [1], & x in [2]
if (board.at(0) == 'x') { boards[2] |= (1 << 22); } else if (board.at(0) == 'W') { boards[0] |= (1 << 22); }
⋅
⋅
⋅
if (board.at(22) == 'x') { boards[2] |= (1 << 0); } else if (board.at(22) == 'W') { boards[0] |= (1 << 0); }
boards[1] = ~(boards[0] | boards[2]) - 4286578688; // Take W's & x's compliment - 4286578688 to get 2163730
printf("%d | %d | %d\n",boards[0], boards[1], boards[2]); // Expected Output: "1351744 | 2163730 | 4873133"
Are there any other tricks to further optimize this process for speed? I'm not as concerned with file size.
Lastly, how would I go about converting the boards[W, B, x] back to a string? (E.g. Player 'W' added a piece to position 22, resulting in boards[] = {1351745, 2163730, 4873132}
. How to convert that to: board = xBWxWxBWxWxxBxxxWxBxxBW
?)
Edit: I got the function to revert back to the board with the following:
char state[23];
for (int i = 0, j = 22; i < 23; ++i, --j) {
if (boards[2] & (1 << j)) { state[i] = 'x'; } else if (boards[0] & (1 << j)) { state[i] = 'W'; } else { state[i] = 'B'; }
}
You mention it the comments that you're a novice. I think there is a lot of background knowledge you need to have to make informed decisions about optimization.
First, compilers are good optimizers. They have built in control flow analysis, inlining, execution reordering, and much more. They are allowed to optimize based on guarantees provided by the c++ standard. Look up undefined behaviour optimization and optimization and pointer aliasing as a starting place for understanding what the compiler can and can't do for optimizations.
Second, there is no way to know whether your changes actually improve the speed of the program without benchmarking it. Like you mentioned, a profiler can help you figure out what is taking time in your program.
Thirdly, you mention that you've heard "strings are slow". A question you should ask is "Compared to what?"
std::string
data is allocated on the heap (unless it qualifies for short string optimization, which is another thing some compilers can do for you). What I assume slow means it that it has to do a load from the heap and a new allocation when growing the string. Allocations are slow and should be avoided if possible for speed. One thing you can de isstd::string::reserve
the max size you expect the string to take. This will allocate memory once instead of dynamically growing it as you add to it which results in at one or more allocations. Look up stack vs heap, allocation cost, and cache misses.TL;DR: Benchmark and experiment, grow your knowledge before you try to outsmart the compiler.