与直接 C# 相比,C# 中 C dll 的重复 dllimport 调用似乎很慢

本文关键字:调用 dllimport dll 相比 | 更新日期: 2023-09-27 18:33:12

我正在使用System.Runtime.InteropServices.DllImportAttribute测试从C#中的dll调用C代码的速度。 C 函数生成一个自定义结构,用值填充它,执行计算,然后返回结果。 这个过程我在一个循环中重复了数十万次,记录了循环前后的即时报价数量。 然后我在直接 C# 中制作了完全相同的函数并重复了这个试验。 直接 C# 方法比使用非托管 DLL 快得多。 为什么? 似乎没有来自非托管的速度增益。

c2cstest.c

#include <stdio.h>
struct test {
double a;
double b;
double c;
};
_declspec(dllexport) double myCfunction(double input) {
struct test one;
one.a = input;
one.b = one.a * one.a;
one.c = one.b * one.a;
return one.c;
}

cl/LD cscstest.c运行Ccode.cs

using System;
using System.Runtime.InteropServices;
class test
{
[DllImport("c2cstest.dll")]
public static extern double myCfunction (double input);
static void Main()
{
double x = 5.25;
double result = 0.0;
long tick1 = DateTime.Now.Ticks;
for(int y = 100000; y > 0; y--)
{
result = myCfunction(x);
}
long tick2 = DateTime.Now.Ticks;
Console.WriteLine("Answer is {0}.  Dllimport took {1} ticks.", result, tick2-tick1);
}
}

输出:答案是 144.703125。 Dllimport花了250000个刻度。运行CScode.cs

using System;
using System.Runtime.InteropServices;
struct test
{
public double a;
public double b;
public double c;
}
class testclass
{
double Mycsfunction (double input)
{
test one;
one.a = input;
one.b = one.a * one.a;
one.c = one.b * one.a;
return one.c;
}
static void Main()
{
double x = 5.25;
double result = 0.0;
testclass ex = new testclass();
long tick1 = DateTime.Now.Ticks;
for(int y = 100000; y > 0; y--)
{
result = ex.Mycsfunction(x);
}
long tick2 = DateTime.Now.Ticks;
Console.WriteLine("Answer is {0}.  Straight CS took {1} ticks.", result, tick2-tick1);
}}

输出:答案是 144.703125。 直CS花了50000个刻度。

补充:在尝试了各种方法之后,我得出了与这家伙相同的结论 调用非托管代码的技术 ,尽管他尝试的方法比我多。

结论:直接的简单函数调用是不值得的(尤其是当它们被循环时(。 将循环放在非托管函数中肯定会有所帮助。 非常大的函数可能是值得的。 无论您尝试多少种不同的方法,编组都不是一种有效的技术。

与直接 C# 相比,C# 中 C dll 的重复 dllimport 调用似乎很慢

基准很难正确;尤其是微观基准,我认为。 首先,你没有衡量你在直接 C# 测试中认为你是什么 - 编译器(或者可能是抖动(认识到结果是循环不变的,所以它只运行一次循环。

以下是我的机器上的结果,您的runCScode.cs保持不变:

c:'temp>csc runCScode.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.

c:'temp>runCScode
Answer is 144.703125.  Straight CS took 10001 ticks.

以下是注释掉for循环线的结果:

c:'temp>csc runCScode.noloop.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.

c:'temp>runCScode.noloop
Answer is 144.703125.  Straight CS took 10001 ticks.

我觉得运行那段代码曾经花费这么长时间很奇怪,所以我猜测出于某种原因实际获取DateTime值有一些开销(我不确定为什么 - 这只是一个猜测(。实际上,我很惊讶非循环在任何时候都需要,因为我会猜到编译器会将整个事情归结为将常量值移动到result中。因此,我在初始化tick1后添加了以下行:

tick1 = DateTime.Now.Ticks;

是的,没错 - 我只是再次用DateTime.Now.Ticks重新加载tick1

然后我再次运行测试:

c:'temp>runCScode.noloop
Answer is 144.703125.  Straight CS took 0 ticks.

(注意:公平地说,大约三分之一的时间我运行没有重新加载的基准测试,tick1我得到了 0 的计数。但大多数运行的计数为 10000+/-1。重新加载的版本tick1始终报告计数为 0 个刻度(。

也就是说,正如几个人在评论中指出的那样,人们并不期望 C 在所有方面都比 C# 快得多,并且对于调用 C 函数的小型操作,P/Invoke 和参数/结果封送处理会产生开销。您还将失去抖动执行优化的机会。 因此,我认为底线是不要担心在 .NET 应用程序中使用 C 来提高性能,除非您有一个需要更快的领域,并且您有理由相信本机 C 或 C++ 可以为您提供一些您无法从 C#(或 C++/CLI 中获得的东西(。

这个原因不会是几个浮点运算。

最后,我认为应该说调用 C 或 C++ DLL 的主要原因之一不一定是为了性能。这是因为在要使用的本机 DLL 中存在一个库(甚至单个 API(,而该功能在 .NET 类中不可用。