C#與C++混合編程及性能分析

概要:

  衆所周知,用C#做界面比C++開發效率要高得多,但在有性能問題的情況下不得不將部分模塊使用C++,這時就需要使用C#與C++混合編程。本文給出了兩種混合編程的方法以及性能對比。

開發環境:

  ThinkPad T430 i5-3230M 2.6G 8G,Win7 64Bit,VS2013(C++開發設置),C++,C#都採用x64平臺,性能驗證使用Release版本。

測試純C++項目性能:

  1. 新建空解決方案:文件|新建|項目|已安裝|模板|其他項目類型|Visual Studio解決方案|空白解決方案

  2. 新建PureCpp項目:右擊解決方案|添加|新建項目|已安裝|Visual C++|Win32控制檯程序,按缺省設置生成項目

  3. 在配置管理器中新建x64平臺,刪除其他平臺

  4. 新建CppFunction,並添加測試代碼,完整代碼如下,程序結果:Result: 1733793664 Elapsed: 109

複製代碼

// CppFunction.h
#pragma once
class CppFunction
{
public:
    CppFunction(){}
    ~CppFunction(){}

    int TestFunc(int a, int b);
};

// CppFunction.cpp
#include "stdafx.h"
#include "CppFunction.h"

class CCalc
{
public:
    CCalc(int a, int b)
    {
        m_a = a;
        m_b = b;
    }

    int Calc()
    {
        if (m_a % 2 == 0){
            return m_a + m_b;
        }
        if (m_b % 2 == 0){
            return m_a - m_b;
        }
        return m_b - m_a;
    }

private:
    int m_a;
    int m_b;
};

int CppFunction::TestFunc(int a, int b)
{
    CCalc calc(a, b);
    return calc.Calc();
}

// PureCpp.cpp : 定義控制檯應用程序的入口點。
//

#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include "CppFunction.h"

using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
    DWORD start = ::GetTickCount();
    CppFunction cppFunction;
    int result = 0;
    for (int i = 0; i < 10000; i++){
        for (int j = 0; j < 10000; j++){
            result += cppFunction.TestFunc(i, j);
        }
    }
    DWORD end = ::GetTickCount();

    cout << "Result: " << result << " Elapsed: " << end - start << endl;

    return 0;
}

複製代碼

測試純Csharp項目性能:

  1. 新建PureCsharp項目:右擊解決方案|添加|新建項目|已安裝|其他語言|Visual C#|控制檯應用程序,按缺省設置生成項目

  2. 在配置管理器中新建x64平臺,刪除其他平臺,去掉【創建新的解決方案平臺】勾選,否則會報x64平臺已經存在

  3. 將C++項目中的代碼複製過來稍作改動,完整代碼如下,程序結果:Result: 1733793664 Elapsed: 729

複製代碼

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PureCsharp
{
    class CCalc
    {
        public CCalc(int a, int b)
        {
            m_a = a;
            m_b = b;
        }

        public int Calc()
        {
            if (m_a % 2 == 0)
            {
                return m_a + m_b;
            }
            if (m_b % 2 == 0)
            {
                return m_a - m_b;
            }
            return m_b - m_a;
        }

        private int m_a;
        private int m_b;
    }

    class CppFunction
    {
        public int TestFunc(int a, int b)
        {
            CCalc calc = new CCalc(a, b);
            return calc.Calc();
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
             DateTime start = System.DateTime.Now;
            CppFunction cppFunction = new CppFunction();
            int result = 0;
            for (int i = 0; i < 10000; i++){
                for (int j = 0; j < 10000; j++){
                    result += cppFunction.TestFunc(i, j);
                }
            }
            DateTime end = System.DateTime.Now;

            System.Console.WriteLine("Result: " + result + " Elapsed: " + (end - start).Milliseconds);
       }
    }
}

複製代碼

性能分析:

  從上面的對比可以看出,同樣的功能,C#的耗時幾乎是C++的7倍,這個例子裏的主要原因是,C++可以使用高效的棧內存對象(CCalc),而C#所有對象只能放在託管堆中。

託管C++混合方式:

  1. 新建C#控制檯項目,命名爲BenchCsharp,使用它來調用C++項目,修改生成目錄爲:..\x64\Release\

  2. 新建C++DLL項目,命名爲DLLCpp,選擇空項目,生成成功,但由於是空項目,不會真正生成dll文件

  3. 在DLLCpp爲空項目時,在BenchCsharp中可以成功添加引用,但當DLLCpp中添加類後,就不能成功添加引用了,已經添加的引用也會顯示警告

  4. 修改DLLCpp項目屬性,右擊項目|屬性|配置屬性|常規|公共語言運行時支持,修改後就可以成功引用了

  5. 在DLLCpp中添加CppFunction類,並複製代碼,完整代碼如下,程序結果:Result: 1733793664 Elapsed: 405

複製代碼

// CppFunction.h
#pragma once
public ref class CppFunction
{
public:
    CppFunction(){}
    ~CppFunction(){}

    int TestFunc(int a, int b);
};

// CppFunction.cpp
#include "CppFunction.h"

class CCalc
{
public:
    CCalc(int a, int b)
    {
        m_a = a;
        m_b = b;
    }

    int Calc()
    {
        if (m_a % 2 == 0){
            return m_a + m_b;
        }
        if (m_b % 2 == 0){
            return m_a - m_b;
        }
        return m_b - m_a;
    }

private:
    int m_a;
    int m_b;
};

int CppFunction::TestFunc(int a, int b)
{
    CCalc calc(a, b);
    return calc.Calc();
}

複製代碼

複製代碼

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BenchCsharp
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime start = System.DateTime.Now;
            CppFunction cppFunction = new CppFunction();
            int result = 0;
            for (int i = 0; i < 10000; i++)
            {
                for (int j = 0; j < 10000; j++)
                {
                    result += cppFunction.TestFunc(i, j);
                }
            } 
            DateTime end = System.DateTime.Now;

            System.Console.WriteLine("Result: " + result + " Elapsed: " + (end - start).Milliseconds);
        }
    }
}

複製代碼

性能分析:

  使用混合編程後,性能得到了一定程度的提升,但比起單純的C++項目,還是差了很多

  將C#主函數中的邏輯轉移到DLLCpp項目中,即添加如下的static方法,C#中只要調用該方法,程序結果:Result: 1733793664 Elapsed: 405

複製代碼

int CppFunction::Test()
{
    DWORD start = ::GetTickCount();
    CppFunction cppFunction;
    int result = 0;
    for (int i = 0; i < 10000; i++){
        for (int j = 0; j < 10000; j++){
            result += cppFunction.TestFunc(i, j);
        }
    }
    DWORD end = ::GetTickCount();

    cout << "Result: " << result << " Elapsed: " << end - start << endl;

    return result;
}

複製代碼

  並沒有變得更快,估計是當使用【公共語言運行時支持】方式編譯C++時,不能發揮C++的性能優勢

DLLImport混合方式:

  1. 新建非空的C++DLL項目,命名爲NativeDLLCpp

  2. 將CppFunction類從PureCpp中複製過來

  3. 代碼如下,運行結果:Result: 1733793664 Elapsed: 125

複製代碼

// NativeDLLCpp.cpp : 定義 DLL 應用程序的導出函數。
//

#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include "CppFunction.h"

using namespace std;

#ifdef __cplusplus
#define TEXPORT extern "C" _declspec(dllexport)
#else
#define TEXPORT _declspec(dllexport)
#endif

TEXPORT int Test()
{
    DWORD start = ::GetTickCount();
    CppFunction cppFunction;
    int result = 0;
    for (int i = 0; i < 10000; i++){
        for (int j = 0; j < 10000; j++){
            result += cppFunction.TestFunc(i, j);
        }
    }
    DWORD end = ::GetTickCount();

    cout << "Result: " << result << " Elapsed: " << end - start << endl;

    return result;
}

複製代碼

複製代碼

    public class NativeDLLCpp
    {
        [DllImport("NativeDLLCpp.dll")]
        public static extern int Test();
    }

    class Program
    {
        static void Main(string[] args)
        {
            DateTime start = System.DateTime.Now;
            int result = NativeDLLCpp.Test();
            DateTime end = System.DateTime.Now;
            System.Console.WriteLine("Result: " + result + " Elapsed: " + (end - start).Milliseconds);
        }
    }

複製代碼

性能分析:

  跟純C++項目性能幾乎一致。

  項目依賴項需要手動設置。

  實現聯調的方法:修改C#項目屬性|調試|啓用本機代碼調試

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章