Live-Forum - Die aktuellen Beiträge
Anzeige
Archiv - Navigation
1908to1912
Aktuelles Verzeichnis
Verzeichnis Index
Übersicht Verzeichnisse
Vorheriger Thread
Rückwärts Blättern
Nächster Thread
Vorwärts blättern
Anzeige
HERBERS
Excel-Forum (Archiv)
20+ Jahre Excel-Kompetenz: Von Anwendern, für Anwender
Inhaltsverzeichnis

Schleife wird gegen Ende immer langsamer

Schleife wird gegen Ende immer langsamer
09.12.2022 20:44:52
Klexy
Guten Morgen allerseits.
Ich habe ein Tabellenblatt "Angebot", das in den Spalten C-I Text enthält, davon sind 300 verbundene Zellen. Die Zellverbünde sind alle einzeilig. In manchen dieser Verbundzellen sind (variable) längere Texte enthalten, die nicht vollständig angezeigt werden, wenn man die Zeilen nur mit "Start > Zellen > Format > Zeilenhöhe automatisch anpassen" auf optimale Höhe bringen will.
Zu diesem Zweck habe ich ein Makro (Button "Zeilenhöhe anpassen"), das in einer Schleife alle Zellen in den Spalten C-I durchgeht und bei Zellverbünden (MergeArea) die Zeilenhöhe anpasst. Das Makro bearbeitet die jeweils markierten Zellen im UsedRange. Daher müssen zum Start des Makros die Spalten C-I markiert sein.
Funktioniert prima, aber ab ca. Zellverbund Nummer 200 wird das Makro deutlich und zunehmend langsamer. Zu Beginn braucht es ca. 1/10 Sekunde pro Zellverbund, bei Nummer 300 dann ca. 7 Sekunden. Ich habe zu diesem Zweck einen Zeitmesser eingebaut, der mir nach jeweils 20 Verbundzellen die Zeit misst. Diese Rundenzeiten werden am Ende in Spalte V ausgegeben (früher gemessene Rundenzeiten werden nach rechts verschoben).
Interessant ist auch, dass das Makro immer unterschiedlich lange braucht, obwohl alle Parameter immer identisch sind und sonst nix auf dem Computer läuft.
Irgendwo läuft da ein Speicher voll.
Musterdatei: https://www.herber.de/bbs/user/156637.xlsm
Hier ist eine weitere Excel-Datei, in der ich den ganzen Makro-Code ausgelesen habe, was die Übersicht erleichtert: https://www.herber.de/bbs/user/156638.xlsm
Ich verwende in der Firma Office 365 (lokale Installation) (=blaue Rundenzeiten) und habe es zuhause auf meinem über 6 Jahre alten Rechner mit Office 2013 laufen lassen, wo es insgesamt schneller, aber auch ab 200 langsamer läuft (=rote Rundenzeiten). Ich meine, dass dieses Problem früher mit Office 2010 nicht aufgetreten ist; das steht mir aber nicht mehr zur Verfügung. Zumindest sind alle meine komplexen Makros mit 2010 viel schneller gelaufen als mit 365. Und auch seltener eingefroren.
Wer weiß, was das ist und vor allem, was man dagegen tun kann?
Bei Hinweisen, die zur Festnahme und Verurteilung des Verdächtigen führen, winken beste Nürnberger Lebkuchen.
Off Topic:
Ein vermutlich damit zusammenhängendes Problem habe ich wenn ich (in einem anderen Fall) zwei lange Makros nacheinander von einem Master-Makro aus aufrufe. Das zweite geht in Schleife alle Zeilen der Tabelle durch, prüft sie und modifiziert sie ggf. Irgendwann heißt es dann "Nicht genügend Speicher vorhanden". Wenn man OK drückt, läuft das Makro trotzdem noch viele Zeilen weiter und sagt dann wieder "Nicht genügend Speicher vorhanden". Das könnte man theoretisch bis zum Ende der Tabelle wiederholen und hätte das gewünschte Ergebnis. Das würde aber ewig dauern.
Wenn ich die beiden Makros nacheinander von Hand anstoße, kommt die Fehlermeldung auch. Wenn ich Excel (ganze Applikation) aber schließe und die Datei neu aufrufe, läuft das zweite Makro problemlos durch (möglicherweise gegen Ende auch langsamer, was ich aber bisher nicht nachgemessen habe).
Irgendwo läuft da ein Speicher voll. Es ist übrigens das Makro, das in der zweiten Datei den Code farblich formatiert.

24
Beiträge zum Forumthread
Beiträge zu diesem Forumthread

Betreff
Datum
Anwender
Anzeige
AW: Schleife wird gegen Ende immer langsamer
09.12.2022 22:17:30
onur
Warum postest du so eine Datei, die nicht läuft? Teste doch mal die gepostete Datei.
AW: Sorry: Hier die korrigierte Musterdatei
09.12.2022 22:43:47
onur
Wie lang braucht der Code bei dir?
AW: Sorry: Hier die korrigierte Musterdatei
10.12.2022 11:57:51
Klexy
ca. 6 Minuten.
Steht in meinem Post, dass das ab Spalte V dokumentiert wird (rot / blau)
AW: Sorry: Hier die korrigierte Musterdatei
10.12.2022 13:24:49
ralf_b
wenn du im Worksheet_Change diverse Manipulationen am Sheet durchführst, dann solltest du die Events vorher ausschalten.
Jede Änderung im Blatt löst das Change erneut aus und du befindest dich in einer rekursiven Schleife. Diese macht den Speicher voll weil du keines der Event-Makros abschliessen kannst.
Das wäre mein Verdacht.
Anzeige
Events ausschalten bringt nix?
10.12.2022 18:45:34
Klexy
Das Worksheet_Change wird zwar aufgerufen, läuft aber ohne wirklich was zu machen durch, weil alle Bedingungen nicht zutreffen.
Ich hab jetzt zusätzlich die Variable ZeilenHoeheLaeuft am Anfang und Ende des Zeilenhöhe-Makros ein- und ausgeschaltet und ins Worksheet_Change folgenden Code an den Anfang gestellt:

If ZeilenHoeheLaeuft = True Then
Exit Sub
End If
Bringt nix.
Dann hab ich an Anfang und Ende des Zeilenhöhe-Makros folgendes eingebaut:

Application.EnableEvents = False
' ... Makro-Code ...
Application.EnableEvents = True 
Bringt auch nix.
Insgesamt ist es mit beiden Lösungen sogar um 45-60% langsamer geworden.
Gibt es noch eine andere Herangehensweise für die Events?
Anzeige
AW: Events ausschalten bringt nix?
10.12.2022 23:06:41
onur
Sind denn 3,60 sec schnell genug?
AW: Events ausschalten bringt nix?
11.12.2022 15:08:25
Klexy
3,60 sec sind schnell genug.
Und wie soll das gehen?
Und was kann ich gegen die Schleifenverlangsamung tun?
AW: Events ausschalten bringt nix?
11.12.2022 15:32:05
onur
Ich weiss gar nicht, ob ich dir das verraten soll. Habe über 4 Std gebraucht, um rauszubekommen, was das Problem ist, weil ich mich da festgebissen habe.
AW: Events ausschalten bringt nix?
11.12.2022 16:49:06
onur
Das Problem sind die bedingten Formatierungen. Durch die ganzen Merge- und Unmergereien werden diese so zerstückelt, dass sie nach jedem Durchlauf immer länger werden (am Schluss sind die Ranges über 3000 Zeichen lang) und immer länger dauern. Das letzte Merge braucht bei mir schon über 3 sec (für ein einziges ".MergeCells = True"), obwohl mein Rechner insgesamt nur 2:00 min braucht).
Lösung: Zu Beginn der Sub "Zeilenhoehe_anpassen" alle bedForms löschen (werden ja sowieso am Ende wieder repariert).
Da diese wieder repariert werden, fällt es auch Keinem auf. Bau mal am Ende, bevor du "BedFor_reparieren_Angebot_CY" aufrufst, ein "Stop" oder ein Breakpoint ein und schau dir mal die bedForms an.
Anzeige
AW: Events ausschalten bringt nix?
11.12.2022 17:12:04
Klexy
Das klingt sehr plausibel. Das werd ich gleich mal in verschiedenen Varianten testen und analysieren.
Weil ich darum weiß, dass die bedingten Formatierungen immer wieder zerbröselt und vervielfacht werden - und die Datei dadurch immer lahmer wird -, hab ich das mit der Reparatur ja eingebaut.
Allerdings bin ich nicht darauf gekommen, das in jeder Runde zu machen bzw. die BedFor zuerst ab Zeile 5 nach unten zu löschen und am Ende wieder auszurollen.
Das Problem saß mal wieder vor dem Rechner.
Schreib mir eine Mail an meinen Namen bei web de, damit ich dir die wohlverdienten Lebkuchen zuschicken kann.
Anzeige
Gerne !
11.12.2022 17:20:56
onur
Ideen dazu
10.12.2022 06:51:36
lupo1
Man kennt z.B. noch die Eigenschaft, dass man manuell in einer Tabelle von hinten her kommend Zeilen löscht, nicht von vorn.
Besser ist es aber, wenn man temporär eine Autofilterspalte neben die Tabelle stellt, diese aktiviert, die Tabelle gefiltert zwischenkopiert, das Filtrat auflöst und die Zwischenkopie plus Leerzeilen dahinter über den Ausgangsdatenbestand kopiert. Dann bleibt alles bei einem Augenblinzel-Moment. In VBA kann man auch gleich mit SpecialCells arbeiten, welches in einigen Fällen den beschriebenen schnellen Umweg erspart.
Hat jetzt nichts mit der Zeilenhöhenjustierung zu tun. Bei der könnte man aber mit einer Hilfsspalte arbeiten, die laufend die Sollhöhe der Verbund-Zellinhalte verwaltet. Und dann jagt man das Makro über diese Angabe, statt dass Excel hier selbst etwas testen muss.
Ich habe nur einmal ein ähnliches Höhenproblem gehabt, habe aber nur mit Einzelzellen mit anzupassenden Höhen gearbeitet. Vielleicht kannst Du aus der Datei in #5 von https://www.clever-excel-forum.de/Thread-Aktiendepot-Berechnungen-erstellen etwas anfangen und Deine Verbünde Richtung Einzelzelle auflösen. Das ist vermutlich schneller.
Anzeige
Danke für die Ideen, aber...
10.12.2022 13:17:09
Klexy
#1: wusste ich nicht, ist aber irgendwie plausibel, denn manuell mach ich das auch so, weil das Auge da besser vorerfassen kann, wer als nächstes kommt.
#2: versteh ich nicht ganz, aber mit SpecialCells arbeite ich bequemlichkeitshalber, wenn ich sie brauche.
#3: das hab ich schon probiert. Das geht natürlich irre schnell. So schnell, dass man keine echten Rundenmessungen machen kann. Aber das setzt voraus, dass die Soll-Höhen bekannt sind. Es gibt aber auch einige Zeilen mit variabler Höhe. Im Prinzip kann jede Höhe geändert werden. Und für den Anwender ist es nicht leistbar, bei fast 400 Zeilen optisch herauszufinden, ob eine Zeile möglicherweise nicht vollständig zu sehen ist.
Daher muss ich eigentlich schon grundsätzlich alle Zellen einzaln nach Bedarf anpassen.
#4: in Einzelzellen auflösen geht nicht. Es ist variabler Fließtext. Manche, die definitiv nie eine Zeile überschreiten, könnte ich als Einzelzellen führen, aber das sind nicht besonders viele.
Ich könnte die Zeit also durchaus noch reduzieren, es bleibt aber die grundsätzliche Frage, warum eine Schleife mit der Zeit immer langsamer läuft. Wo läuft da ein Speicher voll und wie kann man ihn im laufenden Prozess leeren?
Anzeige
Rekursion
10.12.2022 13:23:23
lupo1
entweder beabsichtigt, unbeabsichtigt oder implizit.
Wenn ich mit Performance nicht zufrieden bin, fange ich neu an zu denken, und am Ende lag der Fehler dann bei mir. So wird es hier auch sein.
AW: Rekursion
10.12.2022 18:46:23
Klexy
Zweifellos liegt es an mir.
Und ich hab schon viele neue Wege durchprobiert bis ich hier angekommen bin.
Aber nur "Rekursion" bringt mich nicht auf die Lösung.
Rekursion als Fehler, nicht als Lösung
11.12.2022 17:43:39
lupo1
Zeilenhöhe mit Textfeld ermitteln
10.12.2022 15:47:19
Daniel
Hi
um benötigte Zeilenhöhen oder Spaltenbreiten von Zellen zu ermitteln, kann man auch Textfelder und deren AutoSize-Funktion verwenden.
Das erfordert dann für die Berechnung keine Eingriffe ins Tabellenblatt und ist daher ggf schneller und einfacher zu realisieren:

Sub Makro1()
Dim TF As Shape
Dim ZHalt As Double, ZHneu
Dim Zelle As Range
Dim Bereich As Range
Dim z As Long
Dim zEnde As Long
Dim zStart As Long
With ActiveSheet.UsedRange
zStart = 2
zEnde = .Rows.Count
.EntireRow.AutoFit
End With
'--- Textfeld für Zeilenhöhenermittlung erstellen und einrichten
Set TF = ActiveSheet.Shapes.AddLabel(msoTextOrientationHorizontal, 1, 1, 10, 10)
With TF.TextFrame2
.MarginLeft = 1
.MarginTop = 1
.MarginRight = 1
.MarginBottom = 1
End With
'--- Zeilenhöhe ermitteln
For z = zStart To zEnde
ZHalt = Rows(z).RowHeight
ZHneu = ZHalt
Application.StatusBar = "Zeilenhöhen ermitteln Zeile " & z & " von " & zEnde
For Each Zelle In Intersect(Range("C:I"), Rows(z)).Cells
If Zelle.Value  "" Then 'Zelle nicht leer
With TF.TextFrame2.TextRange
.Font.Size = Zelle.Font.Size
.Characters.Text = Zelle.Value
End With
TF.Width = Zelle.MergeArea.Width
TF.TextFrame2.AutoSize = msoAutoSizeShapeToFitText
If ZHneu  ZHalt Then Rows(z).RowHeight = ZHneu
Next
TF.Delete
Application.StatusBar = False
End Sub
Gruß Daniel
Anzeige
AW: Zeilenhöhe mit Textfeld ermitteln
10.12.2022 17:16:34
Klexy
Das klingt interessant. Das probier ich gleich mal aus und melde mich.
AW: Zeilenhöhe mit Textfeld ermitteln
11.12.2022 15:05:27
Klexy
Das mit dem Textfeld funktioniert sehr schnell und gut.
Nur bei den Zellen, die nur Schriftgröße 6 haben, muss ich noch einen Korrekturfaktor einbauen.
Damit ist zwar die ursprüngliche Frage nach dem volllaufenden Speicher nicht beantwortet, aber im vorliegenden Fall eine schöne Lösung vorhanden.
Schick mir eine Mail an meinen Namen bei web de, damit ich dir die wohlverdienten Lebkuchen zuschicken kann.
Für eine hilfreiche Antwort zur Frage des vollaufenden Speichers sind weitere Lebkuchen ausgeschrieben.
Anzeige
AW: Zeilenhöhe mit Textfeld ermitteln
11.12.2022 16:15:02
Daniel
Hi
Da müßtest du wahrscheinlich noch die Randbreiten (Margins) korrekt einstellen, damit die in der Textbox denen einer Zelle entsprechen. Da habe ich jetzt nur so die 1 reingeschrieben, ohne die exakten werte zu ermitteln.
Gruß Daniel
AW: Zeilenhöhe mit Textfeld ermitteln
11.12.2022 17:01:10
Klexy
Hab's schon herausgefunden: die Textbox hat Calibri, die Zellen haben Arial. Daran liegt es.
AW: Zeilenhöhe mit Textfeld ermitteln
11.12.2022 17:13:06
Klexy
Ursprüngliches Problem wurde von onur gelöst.
AW: Zeilenhöhe mit Textfeld ermitteln
11.12.2022 19:16:03
Klexy
Hier ein wenig an unterschiedliche Schriftgrößen und Textlängen angepasst, weil das leider nicht linear ist.

Sub Zeilenhoehe_ueber_Textfeld()
' ----- Makro von Daniel im Herber-Forum
' (https://www.herber.de/forum/cgi-bin/f_each.pl?idx=1910748)
' --- Info --------------------------------------------------------------------------------
' Update: 09.12.2022
' Dieses Makro stellt bei verbundenen Zellen in EINER Zeile die optimale Höhe ein,
' was mit AutoFit nicht funktionieren würde.
Dim TF As Shape
Dim ZHalt As Double, ZHneu As Double
Dim Zelle As Range, VerbundZelle As Range
Dim z As Long
Dim zEnde As Long
Dim zStart As Long
Dim strikes As Integer, SchriftGroesse As Variant, Faktor As Double
strikes = 0
NochmalEingeben_1:
SchriftGroesse = InputBox(vbCr & vbCr & "Welches ist die ""normale"" Schriftgröße in diesem Blatt?" & vbCr & "", "Variable als Zahl anlegen")
If strikes >= 2 Then ' wenn der Zähler 2 ist, wird das Makro abgebrochen
MsgBox " Makro wird abgebrochen, du Honk, weil du nicht bis  " & ActiveCell.Font.Size & "  zählen kannst!" & _
vbCr & vbCr & _
"________________________________________________"
Exit Sub
Else
If IsNumeric(SchriftGroesse) Then
GoTo LosGehts_1
Else
strikes = strikes + 1 ' wenn keine Schriftgröße eingegeben wurde, wird der Zähler um 1 hochgezählt
MsgBox " Es muss eine ZAHL eingegeben werden." _
& vbCr & vbCr _
& "________________________________________________"
GoTo NochmalEingeben_1 ' wenn der Zähler unter 2 ist, springt das Makro nochmal zur Eingabemaske
End If
End If
LosGehts_1:
' --- Textfeld für Zeilenhöhenermittlung erstellen und einrichten
Set TF = ActiveSheet.Shapes.AddLabel(msoTextOrientationHorizontal, 1, 1, 10, 10)
With ActiveSheet.UsedRange
zStart = 2
zEnde = .Rows.Count
.EntireRow.AutoFit
End With
With TF.TextFrame2
.MarginLeft = 1
.MarginTop = 1
.MarginRight = 1
.MarginBottom = 1
End With
Application.ScreenUpdating = False
' --- Zeilenhöhe ermitteln
For z = zStart To zEnde
ZHalt = Rows(z).RowHeight
ZHneu = ZHalt
Application.StatusBar = "Zeilenhöhen ermitteln Zeile " & z & " von " & zEnde
For Each Zelle In Intersect(Range("C:I"), Rows(z)).Cells
' Zelle.Select
If Zelle.MergeCells = True Then
If Zelle = Zelle.MergeArea(1) Then
If Zelle.Value  "" Then ' Zelle nicht leer
Set VerbundZelle = Zelle
With TF.TextFrame2.TextRange
.Font.Name = Zelle.Font.Name
.Font.Size = Zelle.Font.Size
.Characters.Text = Zelle.Value
End With
TF.Width = Zelle.MergeArea.Width
TF.TextFrame2.AutoSize = msoAutoSizeShapeToFitText
If ZHneu  ZHalt Then
' Rows(z).RowHeight = ZHneu ' für Schrift in angegebener Normalgröße (SchriftGroesse)
If Len(VerbundZelle) > 500 Then
Faktor = (1 + (SchriftGroesse + 1 - VerbundZelle(1).Font.Size) / 10)
Else
Faktor = (1 + (SchriftGroesse + 1 - VerbundZelle(1).Font.Size) / 2 / 10)
End If
Rows(z).RowHeight = ZHneu * Faktor ' Angleichung für kleine Schriftgröße (z.B. Arial 6)
End If
Next
TF.Delete
Application.ScreenUpdating = True
MsgBox "      Feddisch    "
Application.StatusBar = False
End Sub

Anzeige

Links zu Excel-Dialogen

Beliebteste Forumthreads (12 Monate)

Anzeige

Beliebteste Forumthreads (12 Monate)

Anzeige
Anzeige
Anzeige