I have an excel document (10 sheets). 1 sheet has at this point about 1600 rows and about 148 columns of information. There are no formula's in this sheet.
At first I thought it was a problem with my Macro. But it turns out manually inserting a row (or deleting) gives the same problem: a 14 second delay (for ONE ROW!). I've tried deleting all conditional formatting, Data Validation, (deleting hidden graphs that get there data from a different sheet). I've tried turning of Auto Calculation (which I also do in the Macro), I've tried inserting/deleting a row in "Design Mode"> But nothing seems to help.
One version of my file (I have different back-ups) seems to have fixed it. But I cannot reproduce this in my current file. I don't know what the fix was.
I've scoured the internet for solutions but have yet to find one. I'm running on excel 2010. Who knows the trick I need? Or is updating to excel 2016 my best option?
cheers!
The three primary causes of recalculation lag are calculation, event handling (due to a Worksheet_Change) and (to a lesser degree) screen updating.
Put this 'helper' sub procedure in a public module code sheet.
Public Sub appTGGL(Optional bTGGL As Boolean = True)
With Application
.ScreenUpdating = bTGGL
.EnableEvents = bTGGL
.DisplayAlerts = bTGGL
.AutoRecover.Enabled = bTGGL 'no interruptions with an auto-save
.Calculation = IIf(bTGGL, xlCalculationAutomatic, xlCalculationManual)
.CutCopyMode = False
.StatusBar = vbNullString
End With
Debug.Print Timer
End Sub
Use it like this,
sub main()
appTGGL btggl:=false
'do everything you have to do here
appTGGL
end sub
You can add or remove application environment settings as you see fit.
If the problem turns out to be a badly written Worksheet_Change then post it here or on code review for improvement,
quick update: somehow the problem is fixed when I remove to other sheets. These sheets are not connected by formula references in any ways. Both sheets have (flat) information implanted by macro's, but after the macro is done it's just flat information. I've tried disabling al relevant macro's but this didn't solve the issue. Only deleting the other sheets: If i deleted 1 of the sheets the time would go from 14 sec to 7 seconds, and if I deleted the other as well it would go to 0 seconds.
It seems as though somehow these sheets are connected by some kind of calculation (outside of the macros) but I cannot imagine what this might be. Any Ideas?
Related
I've created a macro in Excel for Office 365 to copy data from a source workbook (Source) into an array and append that array to the end of of table in an analysis workbook (Analysis). The Analysis workbook is fairly formula-heavy, so can take a minute to update when changes are made.
At the beginning of the macro, I use Application.Calculation = xlCalculationManual to speed things up and at the end I use Application.Calculation = xlCalculationAutomatic to return them to normal.
Excel hung when I tried to run the macro today. It got stuck at the point that it showed "Calculating (4 threads): 50%" until after several minutes I force-closed it. It did this twice, and upon further investigation -- as expected -- I found that it was occurring at the last line in the macro: Application.Calculation = xlCalculationAutomatic.
I commented out that line and ran the macro again with no issues, then used the F9 key to calculate the sheet and it calculated very quickly.
So my question is: why is the macro hanging when I use xlCalculationAutomatic in the code if Excel has no problem doing a full calculation after the macro has run? Is there a way around this?
Here are the last few lines of the code:
' Paste data; copy and paste formatting
rngPaste.Value = WorksheetFunction.Transpose(arrTemp)
rngTblOldLastRow.Copy
rngPaste.PasteSpecial xlFormats
NormalMode:
Application.ScreenUpdating = True
'Application.Calculation = xlCalculationAutomatic
End Sub
The entry xlCalculationAutomatic makes Excel calculate the result of every individual formula, from the moment that any change to the sheet is made.
E.g. you have an Excel file, containing thousands of formulas, and you have a macro, which modifies or adds hundreds of entries. When you have the mode xlCalculationAutomatic, then all thousands of formulas will be executed at every of the hundreds of entries your macro is modifying or adding. Excel does not hang at such a moment, but it might take a lot of time to finish the task.
Without that xlCalculationAutomatic, your macro will pause the calculation of the formulas, add or modify your hundreds of entries and only calculate all formulas once.
Therefore it is advised not to use this mode while you are working with macros, except, of course, when your macro is using formula results.
A more professional explanation can be found here.
I'm currently working on a document that needs to autofill a selection of cells down an area that is changing, although it will regularly be upwards of 25000 or more rows. The cells that I am attempting to autofill downwards are filled with "complex" formulas, that are configured to work with autofill.
When I have more than 15000 rows, to run one instance of
Worksheets("sheet1").Range("A1:A4").AutoFill Destination:=Range("A1:A" & LastRow), Type:=xlFillDefault
this will take upwards of 20 minutes to completely execute this one line, so if I need to run this 5 times for an example, we're looking an estimated 100 minutes of run time for 5 lines of code.
I'm curious if there's a more efficient way to either use AutoFill, or execute the task I'm looking to achieve.
I haven't really found much that was helpful, I did change my range a few times to see if that increases or reduces times.
If I change LastRow to be equal to say, 50-1000 this line of code runs instantaneously. Once we creep above the 1000 rows, this starts to run incredibly slow.
Ideally, If I can find an additional way to run this that would significantly decrease my run times, that would be great.
There are some boilerplate code I use frequently to squeeze a better performance out of my VBA code:
Sub Test()
Application.EnableEvents = False
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'Code here
....
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Application.ScreenUpdating = False makes your spreadsheet not jump around. Can't provide you with hard data here, but from my personal experience updating the screen slows down your macro quite significantly.
Application.Calculation controls how calculation is handled, and is defaulted to xlCalculationAutomatic. Under this default setting, all formula field gets updated when there is a change in value of a referenced cell. Setting it to xlCalculationManual before running your VBA, then back to xlCalculationAutomatic will ensure that calculations are only performed one time, instead of throughout your script when numbers are changing all the time.
Application.EnableEvents controls codes that is triggered by event (like saving, or activating a worksheet). Just in case, I set it to False.
Remember to set everything back to their default values at the end of your vba, or under your ErrorHandler section of your VBA.
Just FYI, having these two application settings changed makes it a harder to debug your code if you rely on looking at your spreadsheet to debug. Best to completely remove them during debugging, and add them back once you are ready to push to production.
Without your specific formula, it is hard to further optimize. There is one other way you may try that MAY speed things up.
Sub MyAutoFill()
My_Formula = "SUM(1,3)" 'Replace this with your complex formula, remember to escape your inverted commas
Worksheets("sheet1").Range("A1:A" & LastRow) = "=" & My_Formula
End Sub
The above code sets the values of a range to a string the begins with "=", which becomes an excel formula on the worksheet. This MAY be more efficient than autofill, try it out and report back!
This is my first post because in several years of learning VBA, I always been able to find the answers to all my question because some one else beat me to it and because stackexchange rocks. However, I have finally ran into something that I cannot find any answers for...
BACKGROUND:
I have a very complicated Excel workbook that is very VBA intensive (lots of loops and automatic subs). I'm very cautious about referencing cells from VBA because if someone ever shifts a cell without updating the VBA code, problems will ensue. However, I have a couple calculations that need to be done that are far too complex for an in-cell formula, so I've created functions for those things. That avoids any tampering issues and gives me the power I need.
PROBLEM:
But the problem is, since the workbook is so complicated, if I make the UDFs volatile, they will lock up the workbook because it's just looping through way to much data.
But if I don't make them volatile, the function values will not update in the cells.
HOPES AND DREAMS:
I would like to know how to force my UDF to run without being volatile.
NOTE:
I've tried everything I can find with no success, but I am open to ideas. ".calculate" does not work (unless I'm using it wrong). ".calculate = ...automatic" doesn't work either. Creating a "volatile" variable and I toggle externally also doesn't work.
Another solution would be to temporarily turn on function volatility from a different module/sub, but I don't know of a way to do that.
Any help would be greatly appreciated.
Thanks!
(As K.Davis said and xlLogan approved in the comments above) Application.Calculate or (if not working) Application.CalculateFull can help in many cases.
But sometimes it may be necessary to (additionally) mark some UDF(s) as volatile (which xlLogan explicitely did/could not do), especially (like in another case I had) when the dependencies are bi-directional between sheet formulas and VBA code (and like in my case not dependent on some sheet change, but e.g. on the excel filename, that may have been changed during two openings and is recognized and processed during the Workbook_Open() event):
Sub Foo()
Application.Volatile '=> so this sub/function is not considered entirely dependent (deterministic)
' on some cells only, but on other "background" conditions as well
...
End Sub
Also explicitely disabling and enabling events during certain (potentially expensive) recalculation triggering operations/changes (e.g. also possbile via user dialogs/buttons) could help to manage performance issues, e.g. using (some often applicable functionality):
'used with EventsEnable()
Sub EventsDisable()
With Application: .EnableEvents = False: .ScreenUpdating = False: .Calculation = xlCalculationManual: End With
End Sub
'used with EventsDisable()
Sub EventsEnable()
With Application: .EnableEvents = True: .ScreenUpdating = True: .Calculation = xlCalculationAutomatic: End With
End Sub
Some unusal phenomenon started affecting my macro. I have a relatively big (13MB) excel workbook. ThisWorkbook.Save worked just fine. It took like 10 seconds to save the workbook (without any data changes). Now it takes for 2 minutes. Same goes for ThisWorkbook.Close SaveChanges:=True. I am using Excel 2010 on Windows 7.
The strange thing is I can save the workbook manually without any issues, like CTRL+S or using the Save icon in the Excel menu. This way the saving procedure takes less than 10 seconds just as my code used to do it.
The occurence of this unnecessary delay is really bugging me. Do you have any experience with an issue like this? I'm looking for tips to get an approach on the problem.
EDIT:
I've changed to xlsb format as was advised and went a little further with testing. There are some cases when the saving time looks appropiate:
Significant data changes in lots of sheets (48s)
The Save command is triggered from a newly created module without any other changes (5s)
All my charts (150pcs) are deleted (1s). Apparently each chart gives about 1 extra second to the saving time. I know, i should use one dynamic but the damn label bugs... And it was totally fine before!
EDIT 2:
The issue has vanished today. Save command works properly again, saving takes for only a few seconds. Dunno what caused the problem, but I am affraid it will be back and will affect my macro some day again. Excel is definitely quirky!
Since the manual CTRL+S works, I go with the SendKey method until there are better solutions. It does a much faster job than the Save command. Bugs like this are just killing me ...
ThisWorkbook.Activate
SendKeys "^s"
DoEvents
ThisWorkbook.Close
How about this?
Sub Macro1()
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.Calculation = xlCalculationManual
ThisWorkbook.Save
Application.ScreenUpdating = True
Application.EnableEvents = True
Application.Calculation = xlCalculationAutomatic
End Sub
I had similar issue and been struggling with it for a while. Even for every small files 0.5MB it took almost 1 min to save.
SendKeys doesn't work for me in 100% cases. Sometimes workbook was closed Thisworkbook.Close SaveChanges:=False before SendKeys was able to perform action ("^s"), even after adding Application.Wait Now() + TimeValue("0:00:05").
Somehow it's relating (in my case) to Application.CalculateBeforeSave property. When changed to False workbook saved in couple of seconds.
Dim oWB as Workbook
Set oWB = Workbooks.Open(main_path & file_name, False, False)
oWB.Sheets(1).Calculate
Application.CalculateBeforeSave = False
oWB.Save
Application.CalculateBeforeSave = True
oWB.Close savechanges:=False
set oWB = Nothing
I'm trying to select and deselect multiple slicer items from a single slicer without having it trigger an update until all my selections are complete. I'm able to do this pretty simply in the Excel front-end by Ctrl-clicking all the selections I want. However, when I record a macro with this behavior and run it, it updates after each selection/deselection (the recorded macro is just a bunch of .Selection = True/False statements within a With block).
I've tried using the SlicerCaches.VisibleSlicerItemsList function, but that throws a 1004 Application error - even when I've used the SlicerItem.Name field to populate the array:
Dim tntw(0 To 2) as Variant
For i = 0 To 2
tntw(i) = sc.SlicerItems(i + 1).Name
Next i
sc.VisibleSlicerItemsList = tntw
I've also tried setting all dependent PivotTables to manual update for this, as well as trying to set the application.calculation to manual (and switching both back at the end), but neither accomplish what I'm looking for.
Any ideas?
Before your selection put
Application.Calculation = xlmanual
After your selection
Application.Calculation = xlautomatic
This worked perfectly for me. I had the exact same problem.
As #joseph4tw posted in the comments, all that is really needed is the Application.EnableEvents = False line. However, I needed to re-enable events again before the final Slicer was iterated to make the event actually fire. I don't have access to the code any longer (previous job) but imagine the solution involved counting the number of Slicers and at n-1 in the loop call to re-enable events.