背景
我有一个客户端需要Excel VBA代码才能生成移动到VB.NET的公式值。他从事提供财务分析的业务,在这种情况下作为Excel加载项提供。我已将VBA转换为在单独的DLL中运行的VB.NET代码。 DLL被编译为COM服务器,因为Excel可以调用.NET UDF。到目前为止,这么好:Excel单元格有“= foo(Range1,Range2,...)”,VB.NET Com Server的UDF被调用,单元格获得的值与VBA代码的值相匹配。
问题
VB.NET代码慢一点。我可以扩展一系列基于VBA的公式并进行即时计算。我可以扩展相当范围的基于VB.NET的公式,计算需要5-10秒。客户明显变慢,不可接受。
我有几种可能性:
我不认为(2)是真的,因为我将调用附加到Shared New,Public New和Finalize函数中的文件,而我得到的只是:
Shared Sub New
Public Sub New
Finalize
当我打开电子表格时,重复拉伸公式范围,然后关闭电子表格。
我不认为(3)是正确的,因为文件编写显示Application对象只创建一次。
问题
我如何弄清楚花时间的是什么?如何在这种环境中进行分析?是否有明显的增强功能?
在最后一个类别中,我尝试通过使其共享来减少Application对象(用于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数学函数的依赖。我已经取代了Min,Max,Average,Stdev,Small,Percentile,Skew,Kurtosis等等。我的UDF代码更少地调用Excel。不可避免的调用似乎是将Range作为参数并将其转换为.NET数组以供内部使用。
答案 0 :(得分:3)
DLL被编译为COM服务器 因为,Excel可调用.NET UDF必须是
如果是真的,我会同意这一点。但当然,这根本不是真的,为什么我会这样开始......
您可以针对Excel SDK在C ++中编写UDF,并将其作为XLL提供。这是银行定量分析师的普遍做法;事实上,他们似乎很喜欢它,这说明他们作为一个群体很多。
我最近遇到的另一个不那么痛苦的选择是ExcelDNA,它是AFAICT,它提供了讨厌的SDK / XLL位,以便连接.NET DLL。它足够酷,它甚至可以让你加载源代码,而不是构建一个单独的DLL,这对于原型设计非常有用(它利用了CLR实际上包含编译器的事实)。我不知道性能:我没有尝试对它进行基准测试,但它确实似乎绕过了COM Interop问题,众所周知这是一个可怕的问题。
除此之外,我只能支持其他建议:尽可能少地引用您的工作簿,其内容和Excel应用程序。每次通话费用。
答案 1 :(得分:2)
我认真地假设从VB.NET到COM服务器的互操作是通过编组完成的。在VBA中,这些方法被直接调用 - 控制器以几个处理器指令的成本传递给它们,并且看起来非常快。现在,通过编组,完成了一整套额外的工作,每次调用都会遇到严重的开销。您需要严重减少呼叫次数(使每个呼叫更多地工作)或禁用编组并像使用VBA一样工作。有关如何完成后者的详细信息,请参阅this question。
答案 2 :(得分:2)
我最近使用各种产品/方法将数据从Excel移动到.NET。 我尝试的所有.NET方法都比VBA和VB6慢,但最好的方法是能够使用XLL接口,它比Automation接口提供更好的结果。 基准进行了合理优化(将范围转移到阵列等) 结果是(我的基准测试为毫秒)
VB6 COM addin 63
C XLL 37
Addin Express Automation VB.net 170
Addin Express XLL VB.net 100
ExcelDNA XLL CVB.Net 81
Managed XLL提供了相似的时间,但也使得cusom marshallers可以快速。
答案 3 :(得分:2)
CodePlex上的ExcelDna还有更多性能内容:http://exceldna.codeplex.com/Wiki/View.aspx?title=ExcelDna%20Performance。
对于非常简单的函数,通过ExcelDna调用托管函数的开销非常小,允许您每秒进行数十万次UDF调用。
答案 4 :(得分:1)
我的猜测基于通过COM Interop使用Excel的大量经验,它是上下文切换和/或从Excel的内部数据结构到.NET对象的数据映射。
SpreadsheetGear for .NET可能是您的选择。它通过COM Interop比Excel快得多(看看有些客户说here)并且它支持Excel兼容计算和用户定义函数(参见this page上的自定义函数示例)。
如果您想试用,可以免费试用here。
免责声明:我拥有SpreadsheetGear LLC
答案 5 :(得分:1)
我和乔有同样的经历。主要是互操作很慢。
在大多数情况下,这可以通过使用整个范围来解决,而不是单个细胞。 您可以使用.Net数组并在一次调用中将它们传入/传出excel。
e.g。
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
这解决了我见过的所有性能问题
答案 6 :(得分:0)
- DM
答案 7 :(得分:0)
这个问题(7年)真的很晚,但是对于它的价值,我已经在投资银行中使用了5/6个单独的Excel系统,并且在我们所有的Excel系统中都看到了类似的设计模式。描述
是的,它们有一些单元格,其中包含相关数据,例如政府债券价格清单,但它们并不总是通过这块单元格。相反,他们将创建一个驻留在内存中的对象,该对象可以全局访问并用句柄标记。该对象包含单元格内容的副本,因此在分析代码中更容易访问。
所以示例句柄是
'USTreasuries(103450|2016-07-25T15:33)'
可以看出&#39; 103450&#39;是一个对象编号,其唯一性足以从全局范围的字典中获取对象(例如),时间戳表示创建对象的时间,USTreasuries是用户友好的描述。人们可以使用像这样的公式函数创建诸如对象
=CreateHandledObject("USTreasuries",A1:D30)
那个人会写一个分析,它接受这个句柄并在内部获取数据。它要求CreateHandledObject()标记为volatile,您必须将计算转为手动并按代码或用户执行重新计算。
问题源于工作表中无休止的编组数据。我认为这种方法可以帮助您将这个繁琐的元素减少到最低限度。