Ok, Stack Overflow, pour autant que je sache, celui-ci est un doozy.Augmenter lentement le gain de temps lors de l'enregistrement comme dans Excel
J'ai créé une macro activé le fichier excel qui, lorsqu'il est exécuté, effectue les actions suivantes (de haut niveau):
- L'utilisateur sélectionne le fichier de modèle (qui est lui-même macro activée) par boîte de dialogue de fichier
- utilisateur sélectionne les fichiers de données (qui ne sont pas activés pour les macros) également via la boîte de dialogue
- La macro parcourt les fichiers de données et les ouvre un à un, formate les données, migre les données dans une nouvelle feuille de calcul dans un classeur intermédiaire, puis se ferme le fichier de données sans l'enregistrer
- Une fois tous les fichiers bouclés, le classeur intermédiaire est également sauvegardé mais maintenu ouvert
- Une fois que tous les fichiers de données ont été bouclés, chaque feuille du classeur intermédiaire est bouclée, les données de la feuille de calcul courante sont transférées au fichier modèle, et le fichier modèle est enregistré en tant que nouveau fichier étiqueté de façon unique. Une ligne de données dans ce fichier contenant maintenant les données sont copiées dans une feuille récapitulative
(Il est un peu plus compliqué que cela, mais ce sont les aspects importants pour autant que je peux dire)
Voici le problème. le nombre de fichiers de données sélectionnés est de l'ordre de plusieurs milliers (jusqu'à présent, la plus grande exécution que nous avons essayée est de 4000 fichiers). Au fur et à mesure que la macro progresse, le temps nécessaire à l'enregistrement de ces fichiers diminue lentement mais régulièrement. Cela commence à environ cinq secondes, mais à la fin, certains fichiers prennent environ cinq minutes pour être sauvegardés. Le seul indice que j'ai est qu'il y a une fonctionnalité d'itération que j'ai ajouté, une fois que tous les fichiers de données ont été bouclés, il ferme complètement le fichier modèle et ouvre une nouvelle instance avec des paramètres différents, puis recommence le processus. Cela fait revenir le temps de sauvegarde à la normale, puis recommence à croître. Le fichier récapitulatif est également enregistré et fermé au cours de cette étape et un nouveau fichier est ouvert pour la nouvelle itération. J'ai envisagé de fermer et de rouvrir le fichier gabarit chaque centaine de fichiers de données, et je l'implémenterai si je le dois, mais je préférerais obtenir une solution appropriée à ce problème plutôt qu'une approche de pansement. . Si j'ouvre et ferme le fichier template à chaque fois, j'évite le problème de temps, mais alors la macro devient instable, où elle va parfois planter à des points complètement aléatoires pendant la course (mais seulement parfois).
Il s'agit d'un ordinateur qui est isolé de l'Internet ou de tout type de réseau et enregistre dans un lecteur à état solide (nous avons essayé de contrôler un certain nombre de variables).
De toute façon, je suis assez perplexe, donc toutes les suggestions sont les bienvenues!
Option Explicit
Public Sub Example()
Dim Trial As Integer, Trials As Integer, DataSet As Integer
Dim TrialChecker As Boolean
Dim StartTime As Double, WaitTime As Double
Dim StartDate As Date
Dim FileSaveName As String
Dim CopiedDataRange As Range
Dim SummaryRunTimes As Worksheet, Calcs As Worksheet, CutoffsShifts As Worksheet
Dim SheetObjects() As Worksheet
Dim IntermediaryWorkbook As Workbook, Summary As Workbook, Template As Workbook
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'The 1 and Trials are actually set by Lbound and Ubound funcitons, but the premise is the same
For Trial = 1 To Trials
Workbooks.Add
Set Summary = ActiveWorkbook
'I use this one sheet to keep track of how long different parts of the code take to run
Set SummaryRunTimes = Summary.Worksheets(1)
SummaryRunTimes.Name = "Run Times"
SummaryRunTimes.Cells(1, 1).Value = "ID"
SummaryRunTimes.Cells(1, 2).Value = "Data Copy Time (s)"
SummaryRunTimes.Cells(1, 3).Value = "Formula Copy and Calc Time (s)"
SummaryRunTimes.Cells(1, 4).Value = "Summary Copy Time (s)"
SummaryRunTimes.Cells(1, 5).Value = "Save and Cleanup Time (s)"
'sheetnames is defined elsewhere in the code (it's a global variable right now. I intend to change that later).
'It's simply an array of strings with six elements.
For Counter = LBound(sheetnames) To UBound(sheetnames)
Summary.Worksheets.Add
Summary.ActiveSheet.Name = sheetnames(Counter)
Next Counter
'Again, TemplateLocation is defined elsewhere. It's just a string grabbed from a filedialog
Workbooks.Open (TemplateLocation)
Set Template = ActiveWorkbook
Set Calcs = Template.Sheets("Calcs")
Set CutoffsShifts = Template.Sheets("Log Cutoffs & Shifts")
'SheetObjects is simply used as a convenient reference for various sheets in the template file. I found
'it cleaned up the code a bit. Some might say it's unnecessary.
For Counter = LBound(sheetnames) To UBound(sheetnames)
Set SheetObjects(Counter) = Template.Sheets(sheetnames(Counter))
Next Counter
'This is where the parameters for the given trial are set in the template file. Trialchecker is set elsewhere
'(it checks a yes/no dropdown in the original spreadsheet). ParameterAddresses is a range that's grabbed from a
'table object in the original spreadsheet and contains where these parameters go in the template file. These
'will not change depending on the trial, thus column = 1. TrialParameters is in the same table, and are the
'parameters themselves. These DO depend on the trial, and thus the column is equal to the trial number
If TrialChecker = True Then
For Counter = LBound(ParameterAddresses) To UBound(ParameterAddresses)
CutoffsShifts.Range(ParameterAddresses(Counter, 1)).Value = TrialParameters(Counter, Trial)
Next Counter
End If
For DataSet = 1 To IntermediaryWorkbook.Worksheets.Count - 1
'This is where I start my timers
StartTime = Timer
StartDate = Date
'This is where the data is actually copied from the intermediary file into the template. It's always five
'columns wide, but can be any number of rows. the SummaryRunTimes statement is merely grabbing the unique
'identifier of that given worksheet
With IntermediaryWorkbook
Set CopiedDataRange = Calcs.Range("$A$3:$E$" & .Worksheets(Counter).UsedRange.Rows.Count + 1)
CopiedDataRange.Value = IntermediaryWorkbook.Worksheets(Counter).Range("$A$2:$E$" & .Worksheets(Counter).UsedRange.Rows.Count).Value
SummaryRunTimes.Cells(Counter + 1, 1) = Calcs.Cells(3, 1).Value
End With
'First timestamp
SummaryRunTimes.Cells(Counter + 1, 2) = CStr(Round(86400 * (Date - StartDate) + Timer - StartTime, 1))
StartTime = Timer
StartDate = Date
'This statement copies down the formulas that go with the data (which is aobut 100 columsn worth of formuals).
'Throughout this process, calculation is set to manual, so calculation is manually triggered here (Don't ask
'me why I do it twice. If I recall, it's because pivot tables are weird)
Set CopiedFormulaRange = Calcs.Range("$F$3:$KL$" & Calcs.UsedRange.Rows.Count)
CopiedFormulaRange.FillDown
Application.Calculate
Template.RefreshAll
Application.Calculate
'Second timestamp
SummaryRunTimes.Cells(Counter + 1, 3) = CStr(Round(86400 * (Date - StartDate) + Timer - StartTime, 1))
StartTime = Timer
StartDate = Date
'This is a separate function that copies data from the template file into the summary sheet.
'I know you can't see the code, but just know that it only copies six sets of seven cells, so
'as far as I can tell, it's not what is causing the problem. The timestamp supports this idea, as
'it's consistent and short
Call SummaryPopulate(Summary, sheetnames, SheetObjects, r)
r = r + 1
'Third timestamp
SummaryRunTimes.Cells(Counter + 1, 4) = CStr(Round(86400 * (Date - StartDate) + Timer - StartTime, 1))
StartTime = Timer
StartDate = Date
'These following few lines are meant to save the template file as a new file. As I mentioned, this is where
'things get bogged down. FileNameSuffix is a string set via a InputBox. TrialNames is set via the table object
'mentioned above, and is an array of strings.
Application.DisplayAlerts = False
If TrialChecker = True Then
FileSaveName = FolderLocation & "\" & Replace(Calcs.Cells(3, 1).Value, "/", " ") & " OOIP " & FileNameSuffix & " - " & TrialNames(1, Trial) & ".xlsm"
Else
FileSaveName = FolderLocation & "\" & Replace(Calcs.Cells(3, 1).Value, "/", " ") & " OOIP " & FileNameSuffix & ".xlsm"
End If
Template.SaveAs Filename:=FileSaveName, ConflictResolution:=xlLocalSessionChanges
Application.DisplayAlerts = True
'This part clears the copied data and formulas. I added the two Set Nothing lines in the hopes that it would
'solve my problem, but it doesn't seem to do anything
CopiedDataRange.ClearContents
CopiedDataRange.Offset(1, 0).Rows.Delete
Set CopiedDataRange = Nothing
Set CopiedFormulaRange = Nothing
'Fourth and final timestamp
SummaryRunTimes.Cells(Counter + 1, 5) = CStr(Round(86400 * (Date - StartDate) + Timer - StartTime, 1))
'It seems to run a bit better if there's this Wait line here, but I'm not sure why. The WaitTime
'is grabbed from the original worksheet, and is a Double
Application.Wait (TimeSerial(Hour(Now()), Minute(Now()), Second(Now()) + WaitTime))
Next DataSet
'This but simply saves the summary file and then closes that and the template file. Then the process starts anew.
'This seems to be the key for resetting something that reduces the run times.
If TrialChecker = True Then
Summary.SaveAs Filename:=FolderLocation & "\" & "OOIP Summary " & FileNameSuffix & " - " & TrialNames(1, Trial) & ".xlsx"
Else
Summary.SaveAs Filename:=FolderLocation & "\" & "OOIP Summary " & FileNameSuffix & ".xlsx"
End If
Template.Close False
Summary.Close False
Next Trial
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
IntermediaryWorkbook.Close False
End Sub
Dans votre flux de travail, vous n'avez pas d'étape dans laquelle vous enregistrez les fichiers de données. Copiez-vous réellement les données des fichiers de données dans le fichier modèle (un fichier modèle, de nombreux fichiers de données)? Si c'est le cas, la seule sauvegarde qui aura lieu serait le fichier modèle, après que chaque fichier de données a été importé. Fermez-vous le fichier de données après l'importation? Vous pourriez peut-être revoir votre question ci-dessus pour la rendre plus claire. Je pense aussi qu'il sera difficile de faire quelque chose de plus qu'une supposition sans voir le code. – Variatus
À quoi ressemble la mémoire lorsque vous faites autant de fichiers? Comment votre gestionnaire de tâches verrouille-t-il? existe-t-il des centaines de fichiers Excel ouverts, ou votre modèle Excel augmente-t-il sa mémoire? – Moosli
@Variatus Vous avez raison, je m'excuse. J'ai modifié ma question pour essayer de fournir plus de détails. Et je peux inclure mon code, mais je préfère l'éviter si possible. Je devrais en faire beaucoup pour que cela ait un sens dans ce contexte. Ce que je peux dire qui pourrait être utile avant d'arriver à cette étape, c'est que je suis constamment en train de définir et de réinitialiser les variables de plage et de feuille de calcul. Je ne suis pas sûr si cela fait une différence du tout, mais j'ai lu que cela peut encombrer la mémoire un peu (j'ai essayé de les mettre à rien après chaque utilisation, mais cela ne semble pas fonctionner). – apynn