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;
}
View Code

測試純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);
       }
    }
}
View Code

性能分析:

  從上面的對比可以看出,同樣的功能,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();
}
View Code
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);
        }
    }
}
View Code

性能分析:

  使用混合編程后,性能得到了一定程度的提升,但比起單純的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;
}
View Code

  並沒有變得更快,估計是當使用【公共語言運行時支持】方式編譯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;
}
View Code
    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);
        }
    }
View Code

性能分析:

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

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

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM