一、前言

跨平台、跨语言调用本身就是一个很耗费性能的操作,P/Invoke也不例外。但C++/CLI相对P/Invoke来说性能较好。
所以对于P/Invoke来说,我们的优化目标就是尽可能的减少P/Invoke的调用,C++与C#两个层之间要高内聚低耦合,能用函数间调用完成的,就觉不要通过P/Invoke。

以下优化实践将会不断更新。

二、优化实践

2.1 提高std::vector的遍历性能

假如我们定义了如下的c++数据结构:

class TriangleInfo
{
public:
	TriangleInfo() = default;
	TriangleInfo(Vector3 p1, Vector3 p2, Vector3 p3)
		: Pos1(p1), Pos2(p2), Pos3(p3)
	{
	}
	TriangleInfo(Vector3 p1, Vector3 p2, Vector3 p3,Vector3 normal)
		: Pos1(p1), Pos2(p2), Pos3(p3), Normal(normal)
	{
	}

public:
	Vector3 Normal{ 0,0,0 };
	Vector3 Pos1{ 0,0,0 };
	Vector3 Pos2 { 0,0,0 };
	Vector3 Pos3 { 0,0,0 };
};
struct Vector3
{
public:
	Vector3() = default;
	Vector3(float x, float y, float z)
		: X(x), Y(y), Z(z)
	{
	}
	static Vector3 Lerp(const Vector3& a, const Vector3& b, float t)
	{
		t = std::clamp(t, 0.0f, 1.0f); // 限制在 [0,1]
		return Vector3(
			a.X + (b.X - a.X) * t,
			a.Y + (b.Y - a.Y) * t,
			a.Z + (b.Z - a.Z) * t
		);
	}
public:
	float X;
	float Y;
	float Z;
};

同时声明了swig接口文件:

%template(Vector3Vector) std::vector<Vector3>;
%template(TriangleInfoVector) std::vector<TriangleInfo>;

当你在c#侧直接遍历TriangleInfoVector对象时,每遍历一次就会产生一次P/Invoke调用,当数据量很大时,总时间将巨慢无比。
所以我们要想下如何一次性能把收据接收完以减少遍历次数?vector本身提供的data()函数就可以为我们所用,它返回了数组的首地址。
在swig接口文件中增加如下扩展代码:

%define VECTOR_EXTEND(T)
%extend std::vector<T> {
    void* getData() 
    {
        return $self->data();
    }
}
%enddef

VECTOR_EXTEND(TriangleInfo);
VECTOR_EXTEND(Vector3);
VECTOR_EXTEND(float);

在c#侧如下调用即可:

TriangleInfoVector triangles; //you get form somewhere
int triCount = triangles.Count;
TrianglePO[] arr = new TrianglePO[triCount];
unsafe
{
    fixed (TrianglePO* dst = arr)
    {
        Buffer.MemoryCopy((void*)triangles.getData(), dst, triCount * sizeof(TrianglePO), triCount * sizeof(TrianglePO));
    }
}

注意,为了性能c#这里定义了两个与c++结构一样的类型,而没有直接复用c++那边生成的数据结构。

    [StructLayout(LayoutKind.Sequential)]
    public struct Vector3PO
    {
        public float X;
        public float Y;
        public float Z;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct TrianglePO
    {
        public Vector3PO Normal;
        public Vector3PO Pos1;
        public Vector3PO Pos2;
        public Vector3PO Pos3;
    }

[StructLayout(LayoutKind.Sequential)]:按照声明顺序布局内存结构,与c++那边的定义对应起来。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐