背景
我有一位客户,谁需要产生公式值转移到VB.NET的Excel VBA代码。 他是在提供金融分析,在这种情况下交付作为一个Excel加载的业务。 我有翻译,在一个单独的DLL运行VBA到VB.NET代码。 该DLL被编译为一个COM服务器,因为,Excel的调用.NET的UDF必须。 到目前为止,一切都很好:Excel单元格有“= FOO(范围1,范围2,...)”,在VB.NET COM服务器的UDF被调用,并且细胞获得相匹配的VBA代码的值的值。
问题
在VB.NET代码的方式比较慢。 我可以舒展了一系列基于VBA的公式,并得到即时计算。 我可以伸展基于VB.NET-式的比较范围和计算需要5-10秒。 这是明显地更慢的和不能接受的给客户端。
有迹象表明,发生在我几种可能性:
- VBA的本地编译更快,因为没有一个开关的
- DLL可能被加载和卸载的每个UDF呼叫
- 该DLL调用的Excel WorksheetFunction方法和需要的应用程序对象,并创建应用程序对象是昂贵
- 调用从DLL的Excel WorksheetFunction方法是昂贵的
我不认为(2)是真的,因为我把电话给追加到共享的新的公共新建一个文件,并完成功能,以及所有我得到的是:
Shared Sub New
Public Sub New
Finalize
当我打开电子表格,反复拉伸的公式范围内,并关闭该电子表格。
我不认为(3)为真,因为写文件显示,申请对象被创建一次。
问题
我如何找出什么是花时间? 如何在这种环境下个人资料? 是否有明显的改进?
在最后一类,我试图通过使共享,以减少应用程序对象(用于WorkSheetFunction呼叫)的创作的数目:
<Guid("1ECB17BB-444F-4a26-BC3B-B1D6F07D670E")> _
<ClassInterface(ClassInterfaceType.AutoDual)> _
<ComVisible(True)> _
<ProgId("Library.Class")> _
Public Class MyClass
Private Shared Appp As Application ' Very annoying
采取的办法
我试着通过重写我自己的,以减少对Excel的数学函数的依赖。 我把它换成最小值,最大值,平均值,标准偏差,小,百分,斜度,峰度,和几个。 我的UDF代码调用了到Excel少得多。 不可避免的通话似乎都采取了一种范围作为参数和转换,为供内部使用.NET阵列。
Answer 1:
该DLL被编译为一个COM服务器,因为,Excel的调用.NET的UDF必须
一个表明,塞如果属实的一点,我同意。 但当然,这是不是真的,否则为什么我一直就这样开始了......
你可以用C ++你的UDF针对Excel的SDK,并提供它们作为一个XLL,一两件事。 这是一个在银行的数量分析中的普遍做法; 事实上,他们似乎很喜欢它,它说了很多关于他们作为一个群体。
另外,少痛苦的选择,那我是最近才遇到,是ExcelDNA ,其中,AFAICT,提供了讨厌的SDK / XLL位有办法挂钩您的.NET的DLL。 这充分冷却,它甚至可以让你加载的源代码,而不是建立一个单独的DLL,这是伟大的原型(它利用了CLR实际上包含编译器的事实)。 我不知道性能:我没有试图基准,但它似乎避开COM互操作问题,这是众所周知的是可怕的。
除此之外,我只能赞同其他建议:参考您的工作簿,其内容和Excel应用程序尽可能少。 每次通话费用。
Answer 2:
我认真地想从VB.NET互操作的COM服务器通过编组完成。 在VBA的方法被直接调用 - 控制在几个处理器指令的成本传递给他们,看着真快。 现在,随着编组一整套额外的工作已经完成,并每次调用遇到严重的开销。 你需要或者严重降低呼叫的数量(使每个呼叫做更多的工作)或禁用编组和工作,就好像是用VBA。 见这个问题 ,了解如何可能实现后者的细节。
Answer 3:
我最近从基准Excel的移动数据使用各种产品/方法.NET。 所有的.NET方法我试过比VBA和VB6是速度较慢,但其中最好的是能够使用这些都给比自动化接口更好的结果XLL接口。 基准是合理的优化(传输范围,以阵列等),结果是(我的基准的毫秒)
VB6 COM插件63
Ç37 XLL
外接高速自动化VB.net 170
外接快递XLL VB.net 100
ExcelDNA XLL CVB.Net 81
管理XLL给出了相当的时间,但也使cusom marshallers它可以快速。
Answer 4:
没有为ExcelDna CodePlex上的一些表现的东西: http://exceldna.codeplex.com/Wiki/View.aspx?title=ExcelDna%20Performance 。
对于非常简单的功能,通过调用一个ExcelDna管理功能的开销非常小,让您进行每秒几十万UDF调用。
Answer 5:
根据大量的通过COM互操作使用Excel的经验我的猜测是,它是上下文切换和/或数据从Excel的内部数据结构到.NET对象的映射。
的SpreadsheetGear的.NET可能是一个选择。 它比通过COM互操作的Excel(看看一些客户说要快得多这里 )和它支持Excel兼容的计算和用户自定义函数(见自定义功能样本此页 )。
你可以下载一个免费试用在这里 ,如果你想尝试一下。
免责声明:我自己的SpreadsheetGear LLC
Answer 6:
我有同样的经历作为乔。 它主要是缓慢的互操作。
在大多数情况下,这可以通过整个范围,而不是如果单个细胞的工作来解决。 通过使用.NET数组这么做而他们在一个呼叫传递到/从Excel。
如
Dim values(10,10) As object
Dim r As Excel.Range = Me.Range("A1")
r = r.Resize(UBound(values, 1), UBound(values,2))
values = r.Value
For ii = 0 To UBound(values,1)
For jj = 0 To UBound(values,2)
values(ii,jj) = CType(values(ii,jj), Double)*2
Next
Next
r.Value = values
这解决了我所看到的所有的性能问题
Answer 7:
一个念头。 相反,通过Range对象的(这可能是因为每次调用到Ranbe对象可以从.net被整理到Excel),整理所有的参数为基本类型,双打,字符串类型数组,如有必要非类型化的变体数组,并通过他们到.NET的DLL。 这样,你只需要马歇尔的变体。
- DM
Answer 8:
夜已深了这个问题(7岁),但它是值得的,我有5/6单独的Excel系统在投资银行工作,并已经看到了,我将描述他们所有的Excel系统类似的设计模式。
是的,他们有包含相关数据,如政府债券价格清单细胞块,但他们并不总是通过周围细胞的此块。 相反,他们将创建一个驻留在内存中的对象,它是全局可访问的,并标有一个手柄。 该对象包含该单元的内容的副本,所以因此处于解析的代码更容易访问。
因此,一个例子手柄会
'USTreasuries(103450|2016-07-25T15:33)'
其中可以看出,“103450”是一个对象号码,唯一的,足以从一个全局范围字典获取对象(比如说),时间戳被创建对象时表示和USTreasuries是一种用户友好的描述。 人们会创造如用公式函数是这样的对象
=CreateHandledObject("USTreasuries",A1:D30)
在一个会写其中接受该手柄和内部地获取数据的分析。 它要求CreateHandledObject()到被标记易失性和就必须开启计算为手动,并通过代码或由用户执行重新计算。
您的问题从片无尽的编组数据茎。 我觉得这个方法会帮助你这个繁琐的元素减少到最低限度。
文章来源: VB.NET/COM Server code way slower than Excel VBA code