Home > C++ | 遊戲編程 > 試驗SWIG (一) C++ 連接C#和Lua

試驗SWIG (一) C++ 連接C#和Lua

今晚初次嘗試使用 SWIG (Simple Wrapper Interface Generator)。SWIG 是一個能為 C/C++ 程式生成各種 Script 語言 Wrapper 的工具。簡單地說,就是有了一個 C++ 程式,用 SWIG 來連結這個程式和 Script,使腳本語言可以呼叫 C++ 的函數及類別等等。

SWIG 支持大部份 C++ 的功能,甚至 template 也能支持。同時可以生成十數種腳本語言的 wrapper,包括我正在考慮使用的 Lua、Python 和 C# 等。

跟據我的目標,先做一個 3D Vector 的測試。以下是一部份C++代碼 (Vector3.h):

#pragma once
#include 

struct Vector3{
    Vector3();
    Vector3(const Vector3& v);
    Vector3(float x, float y, float z);
    ~Vector3();

    Vector3 operator+(const Vector3& v) const;

    Vector3& operator=(const Vector3& v);
    Vector3& operator+=(const Vector3& v);
    Vector3& operator-=(const Vector3& v);
    Vector3& operator*=(float rhs);
    Vector3& operator/=(float rhs);

    bool operator==(const Vector3& v) const;
    bool operator!=(const Vector3& v) const;

    float GetSquaredLength() const;
    float GetLength() const;
    Vector3 GetNormalized() const;
    void Normalize();

    float x, y, z;
};

inline Vector3::Vector3(const Vector3& v) : 
    x(v.x), y(v.y), z(v.z) 
{
}

inline Vector3& Vector3::operator+=(const Vector3& v) {
    x += v.x;
    y += v.y;
    z += v.z;
    return *this;
}

inline Vector3& Vector3::operator*=(float rhs) {
    x *= rhs;
    y *= rhs;
    z *= rhs;
    return *this;
}
// ...

之後寫一個 SWIG 的 interface 檔案 (Math.i):

%module Math
%{
#include "Vector3.h"
%}

%rename(Add) Vector3::operator+;
%rename(AddEqual) Vector3::operator+=;
%rename(MultiplyEqual) Vector3::operator*=;

// ...

%include "Vector3.h"

最後幾行是因為,以我所知,SWIG 對 C# 並不支持 operator overloading。SWIG 本身是支持 operator overloading 的,或許要深入研究一下 SWIG 的 Wrapper/Proxy 生成過程可不可以加入這功能。

C# 的測試

做 C# 的時候,用

swig -c++ -csharp -o Math_wrap.cxx Math.i

就會新成三個檔案,分別為用來做 C++ Wrapper 的 Math_wrap.cxx、用來做 C# Proxy 的 MathPInvoke.cs 和 Vector3.cs。

接著就把 Math_wrap.cxx 編成 DLL,而 MathPInvoke.cs 和 Vector3.cs 就可以放到 C# 的 Project 裡使用。

剛看到GDC的XNA演講提及.Net的速度,就直接用來比較一下。使用了 SWIG 生成的 Proxy,使用 Vector3 類別和C#寫的沒有分別:

static void Main(string[] args)
{
    Vector3 Position = new Vector3(0, 0, 0);
    Vector3 Velocity = new Vector3(10000, 10000, 10000);
    const float Friction = 0.9f;

    const int count = 10000000;

    System.DateTime start = System.DateTime.Now;

    for (int i = 0; i < count; i++)
    {
        Position.AddEqual(Velocity);
        Velocity.MultiplyEqual(Friction);
    }

    System.DateTime end = System.DateTime.Now;

    System.Console.WriteLine((double)count / (end - start).TotalSeconds);
}

結果留到最後和 Lua 一起比較吧。

Lua 的測試

之前的 Math.i 和其他檔案都不用改,直接執行 swig -c++ -lua -o Math_wrap.cxx math.i,就會生成 Math_wrap.cxx 這個 proxy。和 C# 不同,是不需要 Lua 的 Proxy 的。把這個連結成 Math.dll 後,在Lua 只需一句 Require("Math") 就行了!這實在太簡單!(這麼簡單的功能是在Lua 5.1才有)

同樣的測試用Lua來寫:

require("Math")

local Position = Math.Vector3(0, 0, 0)
local Velocity = Math.Vector3(10000, 10000, 10000)
local Friction = 0.9

local count = 1000000

local start = os.clock()

for i = 1, count do
    Position:AddEqual(Velocity)
    Velocity:MultiplyEqual(Friction)
end

local finish = os.clock()
print(count / (finish - start))

以前看了Lua的書,但沒有寫過,發覺Lua寫起來也很簡單,而且 SWIG 做的 Binding 也不錯。最容易錯的地方就是把 object:method(...) 寫成 object.method(...)。

結果和分析

結果是以每秒執行iteration次數計算,數字越大表示越好。

Case iteration/sec relative performance
C++ 1,544,163 x
C# 519,372 0.336x
Lua 357,653 0.232x
XNA on Xbox360 3,380,000 2.19x

這個比較是有數個問題。一個估計 XNA 的測試裡會有大量的 Particle instances,但我的測試只用了一組 local variables,多 instance 會導致 cache miss,應該會慢許多,另外這亦需要用iterator 或 index 去存取 instances。另外是我的電腦是 Core2 2.4G,應該比 XBox360 快。再者,XNA應該有使用SIMD指令做優化。

不論這個結果準確與否,在對比C++的結果似乎這個最簡單使用SWIG的Wrapper在速度上還存有改善空間。而C#比Lua快應該是基於它使用JIT,而Lua只是一個interpreter。但就這個例子而言,使用Script的速度能達到直接使用C++的二至三成,已經原全超出我的預期。

接下來我想試一試:

  • 使用 C++/CLI 做 Binding 代替 SWIG/PInvoke,看看速度的差異。如果能把 Vector3 當成 value type,速度應該會快 reference type 許多,建立物件的 overhead 亦會減低 (不需要GC)。如果成功,可以把一部份常用類別用 C++/CLI 人手編寫,其他就用 SWIG。
  • 嘗試寫SIMD優化幾個operators,和XNA的結果比較。
  • 測試SWIG對類別、smart pointer、STL等。

Comments:2

Ricky 08-02-25 (一) 20:25

Lua 也有 Jit 的.
不過我認為 Jit 再強, 如果一個 language 沒有 low level construct 如 custom memory management 最終也不會快過 C/C++.
這裡有一篇文章正正道出為何 Java 老是比 C/C++ 低效.

不過 C#/Lua 的性能應當能勝任你的用途. 始終你注意得到的地方, 就不會是問題.

Milo 08-02-25 (一) 23:50

真的不知道 Lua 都有 JIT。

你要找一個腳本語言比 C++ 快,都幾高難度啊(^_^)。不過我覺得 Lua/Java 比 C# 慢的一個原因在於 C# 有 value type。可以把 value type 放在 stack 而不用 GC 。如果在腳本中使用大量 vector/matrix 等運算,效能上可能會是個大差別。

如你所說,我目前只是去了解它們的優點和缺點,並注意可能出現的問題,就可以比較得心應手了。

Comment Form
Remember personal info

Trackbacks:0

Trackback URL for this entry
http://miloyip.seezone.net/wp-trackback.php?p=5
Listed below are links to weblogs that reference
試驗SWIG (一) C++ 連接C#和Lua from Milo的遊戲開發

Home > C++ | 遊戲編程 > 試驗SWIG (一) C++ 連接C#和Lua

Search
Feeds
Meta

Return to page top