web-dev-qa-db-ja.com

ExcelでVBA関数を最適化する方法

私はVBAで関数を作成し、以下に簡略化されたバージョンを提供しました。基本的に、引数を取り、引数の値を使用してシート内の名前付き範囲でvlookupを実行し、vlookedup値を別の関数に渡し、最後に結果を返します。

私はこの関数をよく使用します。ワークブックで50,000回のように。その結果、私のワークブックの計算はかなり遅くなります。

この関数に速度を最適化するために行うことができるいくつかの簡単な変更はありますか?

読みやすさは問題ではありません。私はこれをより速く実行したいだけです。ただし、コードはVBAにとどまる必要があります。

Public Function Yield(Name As String, Price As Double)
    Dim DDate As Double
    Dim ConversionFactor As Double
    DDate = Application.WorksheetFunction.VLookup(Name, Range("LookupRange"), 3, 0)
ConversionFactor = Application.WorksheetFunction.VLookup(Name, Range("LookupRange"), 7, 0)
Yield = 100 * Application.Run("otherCustomFunction",DDate,ConversionFactor,Price)
End Function
1
rvictordelta

最初の戦略:関数自体を最適化する

速度を2倍にする必要があります

Public Function Yield(Name As String, Price As Double)
    Dim Lookup As Range, rw As Integer
    Set Lookup = Range("LookupRange")
    rw = Application.WorksheetFunction.Match(Name, Lookup.Resize(ColumnSize:=1), 0)

    Yield = 100 * Application.Run("otherCustomFunction", Lookup.Cells(rw, 3), Lookup.Cells(rw, 7), Price)
End Function

これは、「LookupRange」という名前の範囲を2回ではなく1回だけ検索し、正しい行を2回ではなく1回だけ検索するためです。

2番目の戦略:範囲を事前に1回だけ取得する

おそらく4倍の速さ

yield関数を使用するコードで範囲を取得する場合、それを1回だけ実行する必要があります。

Public Function Yield(Lookup As Range, Name As String, Price As Double)
    rw = Application.WorksheetFunction.Match(Name, Lookup.Resize(ColumnSize:=1), 0)

    Yield = 100 * Application.Run("otherCustomFunction", Lookup.Cells(rw, 3), Lookup.Cells(rw, 7), Price)
End Function

Public Sub CallingRoutine()
    Dim Lookup As Range, rw As Integer
    Set Lookup = Range("LookupRange")

    ' Some code

    For Each someItem In someSet
        Dim amount As Double, Name As String, Price As Double

        ' Some code to deter;ine name and price

        amount = Yield(Lookup, Name, Price)

        ' Some code that used the yield
    Next someThing
End Sub

以下の辞書で行うように、すべてのルーチンの外部でルックアップを宣言するこの戦略の変形があります。

3番目の戦略:関連するすべての値を辞書に入れる

Yieldを非常に頻繁に呼び出すと、桁違いに速くなります。

  • 名前付き範囲を検索します
  • Excelからすべての値を一度に尋ねます
  • 辞書でNamesを検索します。これは、範囲で検索するよりもはるかに効率的です。

これはコードです:

Public Function Yield(Name As String, Price As Double)
    If LookDict Is Nothing Then
        Set LookDict = New Dictionary

        Dim LookVal As Variant, rw As Integer, ToUse As ToUseType
        LookVal = Range("LookupRange").Value

        For rw = LBound(LookVal, 1) To UBound(LookVal, 1)
            Set ToUse = New ToUseType
            ToUse.Row3Val = LookVal(rw, 3)
            ToUse.Row7Val = LookVal(rw, 7)
            LookDict.Add LookVal(rw, 1), ToUse
        Next rw
    End If

    Set ToUse = LookDict.Item(Name)
    Yield = 100 * Application.Run("otherCustomFunction", _
                  ToUse.Row3Val, ToUse.Row7Val, Price)
End Function

Public Sub CallingRoutine()
    ' Some code

    For Each someItem In someSet
        Dim amount As Double, Name As String, Price As Double

        ' Some code to deter;ine name and price

        amount = Yield(Name, Price)

        ' Some code that used the yield
    Next someThing
End Sub
0
Dirk Horsten

私がするいくつかのこと-

Option Explicit

Public Function Yield(ByVal lookupName As String, ByVal price As Double)
    Dim dDate As Double
    Dim conversionFactor As Double
    Dim foundRow As Long
    foundRow = Application.WorksheetFunction.Match(lookupName, Range("LookupRange"))
    dDate = Range("lookuprange").Cells(foundRow, 3)
    converstionfactor = Range("LookupRange").Cells(foundRow, 7)
    Yield = 100 * otherCustomFunction(dDate, conversionFactor, price)
End Function

引数を渡すときは、デフォルトで、ByValよりも遅いByRefを渡し、必要がないことを確認します。 referenceはそれらにByValを渡すだけです。

matchvlookupよりもはるかに速いかどうかはわかりませんが、matchを使用すると、プロセスが半分になり、必要な行を参照するだけです。

また、変数を 標準のVBA命名規則 名前に変換しました。

また、マクロを呼び出すためにApplication.runは必要ありません。それが引数ByValも渡していることを確認してください

0
Raystafarian