2
\$\begingroup\$

I have a macro I have been using to generate daily fantasy (dfs) lineups for soccer matches. I would like to know where I can speed up my code so I can run more simulations.

Briefly, I have a worksheet in which I try to 'simulate' individual scores for each player in a match (e.g. "ITAvENG" in the code below). This uses the rand() formula in excel so I have to copy and paste values of these scores each time my code runs.

I then call the Excel Solver to calculate the optimal lineup based on those scores (within the constraints of the game/site).

Finally it copies and pastes that lineup into a different worksheet before repeating the process again.

I can currently run 1,000 sims in about 10-15 minutes* but I know I can improve this speed (e.g. I think I am copying and pasting inefficiently?). Can someone advise please?

*There is a lot going on elsewhere in the workbook that I think might be slowing me down as well, I would be happy to explain / share this privately.

Sub Showdown()

Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.DisplayStatusBar = False

Dim StartTime As Double
Dim MinutesElapsed As String
StartTime = Timer

Dim i As Integer

For i = 0 To 999

Calculate

Sheets("ITAvENG").Select
Range("N16:R54").Select
Selection.Copy
Range("S16").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
    :=False, Transpose:=False

SolverReset

SolverAdd CellRef:="$AC$16:$AC$39", Relation:=1, FormulaText:="1"
SolverAdd CellRef:="$Z$3", Relation:=2, FormulaText:="1"
SolverAdd CellRef:="$Z$4", Relation:=2, FormulaText:="5"
SolverAdd CellRef:="$AC$3", Relation:=1, FormulaText:="50000"
SolverAdd CellRef:="$Z$6", Relation:=3, FormulaText:="1"
SolverAdd CellRef:="$Z$7", Relation:=3, FormulaText:="1"

SolverOk SetCell:="$AC$4", MaxMinVal:=1, ValueOf:=0, ByChange:="$AA$16:$AB$39", _
Engine:=2, EngineDesc:="Simplex LP"
SolverAdd CellRef:="$AA$16:$AB$39", Relation:=5, FormulaText:="binary"
SolverSolve True
    
Range("AD16:AD81").Select
Selection.Copy
Sheets("ITAvENG Lineups").Select
Range("$C$12").Offset(i, 0).Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
    :=True, Transpose:=True

Next i

Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayStatusBar = True

MinutesElapsed = Format((Timer - StartTime) / 86400, "hh:mm:ss")

MsgBox "This code ran successfully in " & MinutesElapsed & "minutes", vbInformation

End Sub
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Without knowing what SolverReset, SolverAdd, SolverOK, SolverSolve, and Calculate are doing specifically, there are limits to how much optimization can be suggested. That said, there are some general comments that may be useful that can have some effect on speed, but probably not of the magnitude you are hoping for.

  1. (Best Practice) Always declare Option Explicit at the top of all modules. This forces all variables to be declared explicitly within the module. In this case, adding Option Explicit did not identify any variables being used that were not declared..congrats!...still, it is recommended to avoid a number of hard to debug issues that arise from using undeclared variables (including typos of declared variables).

  2. (Best Practice) When setting and resetting Application flags, it is best to cache the original values, set the values desired, and reset to the prior values at the end of an operation. Further, to absolutely guarantee that the values are reset, always call the 'business' code with error handling protection.

  3. There is no need to Select cells to copy if you are using VBA to assign values/content. Select is required for a 'human-client' to accomplish the operation - but not a 'code-client'. This should provide some speed improvement - though probably minor.

  4. Within the loop, there is only a single statement that depends on the index i (Range("$C$12").Offset(i, 0).Select).

    4a. This means that any statement within the loop that does not depend on the iteration value (or is not modified during each iteration) should be set prior to the loop. The only opportunity here (without knowing the contents of the subroutines listed above) is that Sheets("ITAvENG Lineups") need only be called once (rather than 2000 times).

    4b. Placing all the calls to SolverReset, SolverAdd, SolverOK, SolverSolve, and Calculate within the loop implies that each call is probably modifying content in the CellRef parameter of all the SolverAdd calls during each iteration. If this is not true, then set these cell formulas once outside the loop.

Below is a version that incorporates the comments above.

    Option Explicit

    Sub Showdown()
        Dim originalApplicationCalcSetting As Long
        Dim originalApplicationScreenUpdating As Boolean
        Dim originalApplicationDisplayStatusBar As Boolean
        
        originalApplicationCalcSetting = Application.Calculation
        originalApplicationScreenUpdating = Application.ScreenUpdating
        originalApplicationDisplayStatusBar = Application.DisplayStatusBar

        Application.Calculation = xlCalculationManual
        Application.ScreenUpdating = False
        Application.DisplayStatusBar = False
        
        Dim StartTime As Double
        Dim MinutesElapsed As String
        StartTime = Timer
        
        Dim succeeded As Boolean
        succeeded = False
    On Error GoTo ErrorExit
        
        ShowdownImpl
        
        MinutesElapsed = Format((Timer - StartTime) / 86400, "hh:mm:ss")
        
        MsgBox "This code ran successfully in " & MinutesElapsed & "minutes", vbInformation
        succeeded = True
    ErrorExit:
        Application.Calculation = originalApplicationCalcSetting
        Application.ScreenUpdating = originalApplicationScreenUpdating
        Application.DisplayStatusBar = originalApplicationDisplayStatusBar
        
        If succeeded = False Then
           MsgBox "Error Encountered"
        End If
    End Sub

    Private Sub ShowdownImpl()
        
        Dim i As Integer
        
        Dim itAvENG As Worksheet
        Set itAvENG = Sheets("ITAvENG Lineups")
        
        For i = 0 To 999
            Calculate
            
            itAvENG.Range("N16:R54").Copy
            
            ActiveWorksheet.Range("S16").PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
                :=False, Transpose:=False
            
            SolverReset
            
            'Are these cells modified each iteration?...if not, set these values outside of the loop
            SolverAdd CellRef:="$AC$16:$AC$39", Relation:=1, FormulaText:="1"
            SolverAdd CellRef:="$Z$3", Relation:=2, FormulaText:="1"
            SolverAdd CellRef:="$Z$4", Relation:=2, FormulaText:="5"
            SolverAdd CellRef:="$AC$3", Relation:=1, FormulaText:="50000"
            SolverAdd CellRef:="$Z$6", Relation:=3, FormulaText:="1"
            SolverAdd CellRef:="$Z$7", Relation:=3, FormulaText:="1"
            
            SolverOK SetCell:="$AC$4", MaxMinVal:=1, ValueOf:=0, ByChange:="$AA$16:$AB$39", _
            Engine:=2, EngineDesc:="Simplex LP"
            
            SolverAdd CellRef:="$AA$16:$AB$39", Relation:=5, FormulaText:="binary"
            
            SolverSolve True
                
            ActiveWorksheet.Range("AD16:AD81").Copy
            
            itAvENG.Range("$C$12").Offset(i, 0).PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
                :=True, Transpose:=True
        Next i
    End Sub

    'Subroutines added below to allow the code to compile
    Private Sub SolverReset()
    End Sub

    Private Sub SolverAdd(CellRef As String, Relation As Long, FormulaText As String)
    End Sub

    Private Sub SolverOK(SetCell As String, MaxMinVal As Long, ValueOf As Long, ByChange As String, Engine As Long, EngineDesc As String)
    End Sub

    Private Sub SolverSolve(val As Boolean)
    End Sub

    Private Sub Calculate()
    End Sub
\$\endgroup\$
1
  • 2
    \$\begingroup\$ Also, notice how much more readable it is with proper indention. Makes it much easier to see where loops are and much easier to know what's in the loop and what's not. \$\endgroup\$ Commented Jul 19, 2021 at 10:55

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.