在C中的这个向量函数序列中避免(初学者')分配错误

时间:2016-06-30 16:46:44

标签: c function pointers memory-management struct

背景信息:我已经决定将我在Python中演变的3D图形脚本重新编写为C以提高速度。这涉及到我学习C.有问题的程序的一部分缓存关于3D网格的法线信息。

这里依次调用3个向量运算(非常标准:向量减法得到边向量,交叉乘积和平均值),我希望尽可能快,所以我试图避免存储任何不必要的东西......并且复制我的结构太多次了。但是,我也知道如果我返回指针,我将指向mem空间,它不再有效。

这是我如何尝试编写所有三个函数(一般来说:struct copy in,struct copy out,而不是指针)。

typedef struct vector vector;
struct vector{
    double x,y,z;
};

vector vect(vector a, vector b){
    vector res;
    res.x = b.x - a.x; 
    res.y = b.y - a.y;
    res.z = b.z - a.z;
    return res;
}

vector cross(vector a, vector b){
    vector res;
    res.x = a.y*b.z - a.z*b.y;
    res.y = a.z*b.x - a.x*b.z;
    res.z = a.x*b.y - a.y*b.x;
    return res;
}

vector avg (vector a, vector b){
    vector res;
    res.x = (a.x + b.x)/2;
    res.y = (a.y + b.y)/2;
    res.z = (a.z + b.z)/2;
    return res;
}

这就是它的名字:

m->Tpolynormals[i] = avg(cross( vect(*p->verts[0], *p->verts[1]),
                                vect(*p->verts[1], *p->verts[2]) ),
                         cross( vect(*p->verts[2], *p->verts[3]),
                                vect(*p->verts[3], *p->verts[0]) )
                        );

这是否相当有效还是有更快的方法吗?我知道我可以尝试并且#34;让它工作"但是在这一点上我想确保基础是坚实的。 - 谢谢

编辑:在上面添加了我的结构定义,正如有人明显指出的那样,呃。坐标是双倍的(它是我的3D包输出的),系统是64位。

2 个答案:

答案 0 :(得分:5)

"避免(初学者')分配错误" vs."我希望尽可能快,"

哪个更重要?

如果代码需要尽可能快,请尝试多种方法并对其进行分析,以了解最适合您的方法。你会犯错误。

sizeof vector位于边界区域,为最佳状态提供一般答案,通过值或其地址传递vector。最好兼顾

1)按值传递vector。 OP似乎很清楚。

vector vect(vector a, vector b){
    vector res;
    res.x = b.x - a.x; 
    res.y = b.y - a.y;
    res.z = b.z - a.z;
    return res;
}

2)将vector通过其地址。创建中间结果位置。这似乎是OP不确定的部分。

void V_vect(vector *res, const vector *a, const vector *b){
    res->x = b->x - a->x; 
    res->y = b->y - a->y;
    res->z = b->z - a->z;
}

// usage example
vector res1;
vector res2;
V_vect(&res1, p->verts[0], p->verts[1]);
V_vect(&res2, p->verts[1], p->verts[2]);
vector res3;
V_cross(&res3, &res1, &res2);

V_vect(&res1, p->verts[2], p->verts[3]);
V_vect(&res2, p->verts[3], p->verts[0]);
vector res4;
V_cross(&res4, &res1, &res2);

V_avg(&m->Tpolynormals[i], &res3, &res4);

特别建议避免在同一个呼叫中重复使用内存,如下所示。在功能和代码性能方面,这可能是一个新手的错误。

V_cross(&res2, &res1, &res2);

传递地址时加速的方法是使用restrict。这允许编译器知道调用代码正在使用指向不重叠区域的指针。这允许某些编译器优化。

void V_vect(vector * restrict res, const vector * restrict a, const vector * restrict b){
    res->x = b->x - a->x; 
    res->y = b->y - a->y;
    res->z = b->z - a->z;
}

对于restrict,下面第一个调用是未定义的行为,因为它与vector重叠。

// V_cross(&res2, &res1, &res2);  // bad
V_cross(&res4, &res1, &res2);  // good

尝试各种方法(包括@Jonathan Leffler compound literal idea@Jonathan Leffler inline idea)并使用适合您的方法。

答案 1 :(得分:2)

如果要最小化复制的数据量,可以将指针传递给输入和输出参数。但这意味着您不能像上面那样将函数调用链接在一起,这意味着您需要使用临时变量来保存每次调用的结果。

例如:

void vect(vector *a, vector *b, vector *res){
    res->x = b->x - a->x; 
    res->y = b->y - a->y;
    res->z = b->z - a->z;
}

// similarly for the other two

然后给他们打电话:

vector vect1, vect2, vect3, vect4, cross1, cross2;
vect(p->verts[0], p->verts[1], &vect1);
vect(p->verts[1], p->verts[2], &vect2);
vect(p->verts[2], p->verts[3], &vect3);
vect(p->verts[3], p->verts[0], &vect4);
cross(&vect1, &vect2, &cross1);
cross(&vect3, &vect4, &cross2);
avg(&cross1, &cross2, &m->Tpolynormals[i]);