C ++,使用一个字节来存储两个变量

时间:2010-04-16 20:25:43

标签: c++ performance pointers chess

我正在研究棋盘的表示,我打算将它存储在32字节数组中,其中每个字节将用于存储两个部分。 (这样每件只需要4位)

以这种方式执行此操作会导致访问电路板特定索引的开销。 您是否认为,可以使用此代码进行优化,还是可以使用完全不同的访问索引的方法?

C ++

char getPosition(unsigned char* c, int index){
    //moving pointer
    c+=(index>>1);

    //odd number
    if (index & 1){
        //taking right part
        return *c & 0xF;
    }else
    {
        //taking left part
        return *c>>4;
    }
}


void setValue(unsigned char* board, char value, int index){
    //moving pointer
    board+=(index>>1);

    //odd number
    if (index & 1){
        //replace right part
                 //save left       value only 4 bits
        *board = (*board & 0xF0) + value;
    }else
    {
        //replacing left part
        *board  = (*board & 0xF) + (value<<4);
    }
}


int main() {

    char* c = (char*)malloc(32);

    for (int i = 0; i < 64 ; i++){
        setValue((unsigned char*)c, i % 8,i);
    }

    for (int i = 0; i < 64 ; i++){
        cout<<(int)getPosition((unsigned char*)c, i)<<" ";

        if (((i+1) % 8 == 0) && (i > 0)){
            cout<<endl;
        }


    }


    return 0;
}

我对你关于国际象棋表示的观点以及上述方法的优化同样感兴趣,作为一个独立的问题。

非常感谢

修改

感谢您的回复。前段时间我创建了跳棋游戏,我使用的是64字节的棋盘表示。这次我尝试了一些不同的方法,只是为了看看我喜欢什么。记忆不是一个大问题。比特板肯定在我的名单上尝试。感谢

6 个答案:

答案 0 :(得分:9)

这是过早优化的问题。你的国际象棋棋盘需要64个字节来存储,现在需要32个。这真的让你感到沮丧吗?你是否真的分析了情况,看看你是否需要保存那段记忆?

假设您使用了最优的搜索方法之一,直接AB搜索深度D而没有启发式,并且您在搜索之前生成所有可能的移动位置,那么您的电路板所需的绝对最大内存将是sizeof (板)* W * D.如果我们假设一个相当大的W = 100和大D = 30那么你将在深度为D的内存中有3000块板.64k对32k ......它真的值得吗? / p>

另一方面,您增加了访问主席[位置]所需的操作量,每次搜索会调用数百万次。

建立国际象棋时,你最终要找的主要是cpu周期,而不是内存。如果您的目标是手机或其他东西,这可能会有所不同,但即便如此,在您达到足够的深度以引起任何内存问题之前,您会更加担心速度。

至于我喜欢哪种表示方式......我喜欢位板。没有做过很多严肃的测量,但我确实比较了我制造的两个引擎,一个位板和一个阵列,而一个位置更快,可以达到比另一个更大的深度。

答案 1 :(得分:3)

让我成为第一个指出潜在错误的人(取决于编译器和编译器设置)。而错误就是为什么过早优化是邪恶的:

   //taking left part
    return *c>>4;

如果* c为负数,则&gt;&gt;可能会重复负高位。即二进制:

0b10100000 >> 4 == 0b11111010

对于某些编译器(即C ++标准将其留给编译器来决定 - 是否携带高位,以及char是有符号还是无符号)。

如果你确实希望继续使用你的打包位(并且让我说你可能不应该打扰,但这取决于你),我建议将打包的位包装成一个类,并覆盖[]这样

board[x][y] 

为您提供解压缩的位。然后,您可以轻松打开和关闭打包,并在任何一种情况下使用相同的语法。如果你内联运算符重载,它应该与你现在的代码一样高效。

答案 2 :(得分:2)

嗯,64字节是一个非常少量的RAM。你最好只使用char [8] [8]。也就是说,除非您计划存储 ton 的棋盘。使用char [8] [8]可以更容易(更快)地访问电路板并对其执行更复杂的操作。

如果您仍然有兴趣将电路板存储在打包的表示中(无论是练习还是存储大量电路板),我说你对于位操作“做得对”。如果您希望使用inline关键字加快速度,则可能需要考虑内联访问者。

答案 3 :(得分:0)

空间是否足够考虑你不能只使用一个完整的字节来表示一个正方形?这将使访问更容易在程序上进行,并且由于不需要位操作,因此更可能更快。

否则为了确保一切顺利,我会确保所有类型都是无符号的:getPosition返回unsigned char,并用“U”(例如0xF0U)限定所有数字文字,以确保它们始终被解释为无符号。很可能你在签名方面没有任何问题,但是为什么要冒一些意外行为的架构呢?

答案 4 :(得分:0)

不错的代码,但如果您真的非常了解性能优化,那么您应该了解有关特定CPU架构的更多信息。

AFAIK,您可能会发现将棋子存储在8个字节中会更有效率。即使您进行15次深度递归,L2缓存大小也不会成为约束,但RAM错位可能。我猜想对棋盘的正确处理将包括Expand()和Reduce()函数,以在算法的不同部分之间在板表示之间进行转换:一些在紧凑表示上可能更快,反之亦然。例如,缓存和涉及通过两个相邻单元的组合进行散列的算法可能对紧凑结构有利,其他所有都没有。

如果性能如此重要,我还会考虑开发一些辅助硬件,比如一些FPGA板或一些GPU代码。

答案 5 :(得分:0)

作为一名国际象棋选手,我可以告诉你:除了每件作品之外,还有更多的位置。你必须考虑其他一些事情:

  1. 下一步必须移动哪一方?
  2. 白色和/或黑色的城堡王和/或后翼?
  3. 一个棋子可以被传递吗?
  4. 自上一次典当移动和/或捕获移动以来经过了多少次移动?
  5. 如果您用来表示某个职位的数据结构不能反映这些信息,那么您就遇到了大麻烦。