C结构奇怪的行为

时间:2010-10-12 18:01:23

标签: c opencl

我有一些涉及结构定义的长源代码:

struct exec_env {

    cl_program* cpPrograms;
    cl_context cxGPUContext;

    int cpProgramCount;
    int cpKernelCount;
    int nvidia_platform_index;
    int num_cl_mem_buffs_used;
    int total;

    cl_platform_id cpPlatform;
    cl_uint ciDeviceCount;
    cl_int ciErrNum;
    cl_command_queue commandQueue;

    cl_kernel* cpKernels;
    cl_device_id *cdDevices;
    cl_mem* cmMem;
};

奇怪的是,我程序的输出取决于我声明这个结构的组件的顺序。为什么会这样?

编辑:

更多代码:

int HandleClient(int sock) {

struct exec_env my_env;

int err, cl_err;
int rec_buff [sizeof(int)];

log("[LOG]: In HandleClient. \n");

my_env.total = 0;

//in anticipation of some cl_mem buffers, we pre-emtively init some. Later, we should have these
//grow/shrink dynamically.
my_env.num_cl_mem_buffs_used = 0;
if ((my_env.cmMem = (cl_mem*)malloc(MAX_CL_BUFFS * sizeof(cl_mem))) == NULL)
    {
        log("[ERROR]:Failed to allocate memory for cl_mem structures\n");
        //let the client know
        replyHeader(sock, MALLOC_FAIL, UNKNOWN, 0, 0);
        return EXIT_FAILURE;
    }

my_env.cpPlatform = NULL;
my_env.ciDeviceCount = 0;
my_env.cdDevices = NULL;
my_env.commandQueue = NULL;
my_env.cxGPUContext = NULL;

while(1){

    log("[LOG]: Awaiting next packet header... \n");


    //read the first 4 bytes of the header 1st, which signify the function id. We later switch on this value
    //so we can read the rest of the header which is function dependent.
    if((err = receiveAll(sock,(char*) &rec_buff, sizeof(int))) != EXIT_SUCCESS){
        return err;
    }

    log("[LOG]: Got function id %d \n", rec_buff[0]);
    log("[LOG]: Total Function count: %d \n", my_env.total);
    my_env.total++;

    //now we switch based on the function_id
    switch (rec_buff[0]) {
    case CREATE_BUFFER:;
            {
                //first define a client packet to hold the header
                struct clCreateBuffer_client_packet my_client_packet_hdr;
                int client_hdr_size_bytes = CLI_PKT_HDR_SIZE + CRE_BUFF_CLI_PKT_HDR_EXTRA_SIZE;

                //buffer for the rest of the header (except the size_t)
                int header_rec_buff [(client_hdr_size_bytes - sizeof(my_client_packet_hdr.buff_size))];
                //size_t header_rec_buff_size_t [sizeof(my_client_packet_hdr.buff_size)];
                size_t header_rec_buff_size_t [1];

                //set the first field
                my_client_packet_hdr.std_header.function_id = rec_buff[0];

                //read the rest of the header
                if((err = receiveAll(sock,(char*) &header_rec_buff, (client_hdr_size_bytes - sizeof(my_client_packet_hdr.std_header.function_id) - sizeof(my_client_packet_hdr.buff_size)))) != EXIT_SUCCESS){
                    //signal the client that something went wrong. Note we let the client know it was a socket read error at the server end.
                    err = replyHeader(sock, err, CREATE_BUFFER, 0, 0);
                    cleanUpAllOpenCL(&my_env);
                    return err;
                }

                //read the rest of the header (size_t)
                if((err = receiveAll(sock, (char*)&header_rec_buff_size_t, sizeof(my_client_packet_hdr.buff_size))) != EXIT_SUCCESS){
                    //signal the client that something went wrong. Note we let the client know it was a socket read error at the server end.
                    err = replyHeader(sock, err, CREATE_BUFFER, 0, 0);
                    cleanUpAllOpenCL(&my_env);
                    return err;
                }

                log("[LOG]: Got the rest of the header, packet size is %d \n", header_rec_buff[0]);
                log("[LOG]: Got the rest of the header, flags are %d \n", header_rec_buff[1]);
                log("[LOG]: Buff size is %d \n", header_rec_buff_size_t[0]);

                //set the remaining fields
                my_client_packet_hdr.std_header.packet_size = header_rec_buff[0];
                my_client_packet_hdr.flags = header_rec_buff[1];
                my_client_packet_hdr.buff_size = header_rec_buff_size_t[0];

                //get the payload (if one exists)
                int payload_size = (my_client_packet_hdr.std_header.packet_size - client_hdr_size_bytes);
                log("[LOG]: payload_size is %d \n", payload_size);
                char* payload = NULL;

                if(payload_size != 0){

                    if ((payload = malloc(my_client_packet_hdr.buff_size)) == NULL){
                            log("[ERROR]:Failed to allocate memory for payload!\n");
                            replyHeader(sock, MALLOC_FAIL, UNKNOWN, 0, 0);
                            cleanUpAllOpenCL(&my_env);
                            return EXIT_FAILURE;
                    }

                    if((err = receiveAllSizet(sock, payload, my_client_packet_hdr.buff_size)) != EXIT_SUCCESS){
                        //signal the client that something went wrong. Note we let the client know it was a socket read error at the server end.
                        err = replyHeader(sock, err, CREATE_BUFFER, 0, 0);
                        free(payload);
                        cleanUpAllOpenCL(&my_env);
                        return err;
                    }

                }

                //make the opencl call
                log("[LOG]: ***num_cl_mem_buffs_used before***: %d \n", my_env.num_cl_mem_buffs_used);
                cl_err = h_clCreateBuffer(&my_env, my_client_packet_hdr.flags, my_client_packet_hdr.buff_size, payload, &my_env.cmMem);
                my_env.num_cl_mem_buffs_used = (my_env.num_cl_mem_buffs_used+1);
                log("[LOG]: ***num_cl_mem_buffs_used after***: %d \n", my_env.num_cl_mem_buffs_used);

                 //send back the reply with the error code to the client
                log("[LOG]: Sending back reply header \n");

                if((err = replyHeader(sock, cl_err, CREATE_BUFFER, 0, (my_env.num_cl_mem_buffs_used -1))) != EXIT_SUCCESS){
                   //send the header failed, so we exit
                   log("[ERROR]: Failed to send reply header to client, %d \n", err);
                   log("[LOG]: OpenCL function result was %d \n", cl_err);
                   if(payload != NULL) free(payload);
                   cleanUpAllOpenCL(&my_env);
                   return err;
                }

                //now exit if failed
                if(cl_err != CL_SUCCESS){
                    log("[ERROR]: Error executing OpenCL function clCreateBuffer %d \n", cl_err);
                    if(payload != NULL) free(payload);
                    cleanUpAllOpenCL(&my_env);
                    return EXIT_FAILURE;
                }

            }
            break;

现在真正有趣的是调用h_clCreateBuffer。该功能如下

int h_clCreateBuffer(struct exec_env* my_env, int flags, size_t size, void* buff, cl_mem* all_mems){

/*
 * TODO:
 * Sort out the flags.
 * How do we store cl_mem objects persistantly? In the my_env struct? Can we have a pointer int the my_env
 * struct that points to a mallocd area of mem. Each malloc entry is a pointer to a cl_mem object. Then we
 * can update the malloced area, growing it as we have more and more cl_mem objects.
 */

//check that we have enough pointers to cl_mem. TODO, dynamically expand if not
if(my_env->num_cl_mem_buffs_used == MAX_CL_BUFFS){
    return CL_MEM_OUT_OF_RANGE;
}

int ciErrNum;
cl_mem_flags flag;
if(flags == CL_MEM_READ_WRITE_ONLY){
    flag = CL_MEM_READ_WRITE;
}

if(flags == CL_MEM_READ_WRITE_OR_CL_MEM_COPY_HOST_PTR){
    flag = CL_MEM_READ_WRITE | CL_MEM_COPY_HOST_PTR;
}

log("[LOG]: Got flags. Calling clCreateBuffer\n");
log("[LOG]: ***num_cl_mem_buffs_used before in function***: %d \n", my_env->num_cl_mem_buffs_used);
all_mems[my_env->num_cl_mem_buffs_used] = clCreateBuffer(my_env->cxGPUContext, flag , size, buff, &ciErrNum);
log("[LOG]: ***num_cl_mem_buffs_used after in function***: %d \n", my_env->num_cl_mem_buffs_used);

log("[LOG]: Finished clCreateBuffer with id: %d \n", my_env->num_cl_mem_buffs_used);
//log("[LOG]: Finished clCreateBuffer with id: %d \n", buff_counter);
return ciErrNum;
}

第一次循环while循环时,my_env-> num_cl_mem_buffs_used增加1.但是,下次循环循环时,在调用clCreateBuffer之后,my_env-> num_cl_mem_buffs_used的值将重置为0。当我改变我声明结构成员的顺序时,不会发生!思考?请注意,我省略了其他case语句,所有这些语句都很相似,即更新结构成员。

7 个答案:

答案 0 :(得分:2)

好吧,如果您的程序转储结构类型对象的原始内存内容,那么输出显然取决于结构中字段的顺序。所以,这是一个明显的场景,将创建这样的依赖。还有很多其他的。

为什么您的程序输出取决于该顺序,您感到惊讶?一般来说,这种依赖并不奇怪。如果您根据对其余代码的了解做出判断,那么我理解。但是这里的人没有这样的知识,我们也没有心灵感应。

答案 1 :(得分:1)

很难说。也许你可以发布一些代码。如果我不得不猜测,我会说你在这个结构中输入一些输入文件(由字节组成)。在这种情况下,您必须为您的结构声明正确的顺序(通常由某些协议标准化),以便正确地施放或冒使数据无效的风险。

例如,如果您的文件由两个字节组成,并且您正在将文件转换为结构,则需要确保结构已正确定义顺序以确保正确的数据。

struct example1
{
   byte foo;
   byte bar;
};

struct example2
{
   byte bar;
   byte foo;
};

//...

char buffer[];
//fill buffer with some bits

(example1)buffer; 
(example2)buffer;
//those two casted structs will have different data because of the way they are    defined.

在这种情况下,根据某些标准,“缓冲区”将始终以相同的方式填充。

答案 2 :(得分:1)

当然输出取决于订单。结构中的字段顺序很重要。

此处发布的其他答案的另一种解释是:编译器可能在结构中的字段之间添加填充,特别是如果您使用的是64位平台。

答案 3 :(得分:0)

如果您没有使用二进制序列化,那么最好的选择是无效指针问题。像+1错误,或一些无效的指针算术可以导致这一点。但没有代码就很难知道。即使使用代码,仍然很难知道。您可以尝试使用某种指针验证/跟踪系统来确定。

答案 4 :(得分:0)

其他猜测

通过更改结构中具有不同未初始化值的顺序。指针为零或不为零,例如

你以某种方式设法超越一个项目(通过施法)和爆炸后期项目。根据订单不同的项目会受到爆炸

答案 5 :(得分:0)

如果您的代码在C89上使用“旧式”初始值设定项,则可能会发生这种情况。举个简单的例子

struct toto {
  unsigned a;
  double b;
};
.
.
toto A = { 0, 1 };

如果您交换定义中的字段,这仍然是一个有效的初始化程序,但您的字段初始化完全不同。 Modern C,AKA C99,为此指定了初始化器:

toto A = { .a = 0, .b = 1 };

现在,即使重新排序字段或插入新字段,您的初始化仍然有效。

这是一个常见的错误,可能是我在许多C89程序中观察到的初始化恐惧症的起源。

答案 6 :(得分:0)

您的结构中有14个字段,因此编译器和/或标准C库可以在输出期间对它们进行排序,这是 14!的可能方式。

如果从编译器设计者的角度考虑,那顺序应该是什么?随机肯定没用。唯一有用的顺序是您声明结构字段的顺序。