Excel VBA Performance optimieren – Der vollständige Leitfaden
Warum VBA-Makros langsam werden, die 10 wirksamsten Optimierungen mit Vorher/Nachher-Code, ein Copy-Paste-Boilerplate, Benchmarking-Muster und die häufigsten Performance-Fehler.
Langsame Makros kosten Zeit und Nerven. Ein Makro, das 10.000 Zeilen in 8 Minuten verarbeitet, wird früher oder später durch manuelle Arbeit ersetzt. Mit den richtigen Techniken erledigt dasselbe Makro dieselbe Aufgabe in unter 5 Sekunden – ohne die Logik zu verändern.
Dieser Leitfaden erklärt, warum VBA langsam wird, zeigt die 10 wirksamsten Optimierungen mit Vorher/Nachher-Code und beantwortet die häufigsten Performance-Fragen.
Optimierten Code ohne Handarbeit? Der VBA Code Generator schreibt Performance-optimierte Makros mit Arrays, Toggles und sauberem Error-Handling – in Sekunden.
Warum VBA-Makros langsam werden
VBA selbst ist schnell. Der Flaschenhals liegt fast immer in der Kommunikation zwischen VBA und der Excel-Engine. Jeder Lese- oder Schreibzugriff auf eine Zelle, jede Range-Operation und jede ausgelöste Neuberechnung überquert die COM-Grenze von VBA nach Excel – ein teurer Umweg.
Es gibt zwei Arten von Overhead:
Excel-Engine-Overhead
Nach jeder Zelländerung zeichnet Excel den Bildschirm neu, berechnet alle abhängigen Formeln und feuert Worksheet-Events. In einer Schleife über 50.000 Zeilen sind das 50.000 Bildschirmaktualisierungen und 50.000 Neuberechnungszyklen – sofern man sie nicht explizit abschaltet.
COM-Round-trip-Overhead
Jeder Zugriff auf .Value, .Range oder .Cells ist ein Aufruf aus der VBA-Laufzeitumgebung in das Excel-Objektmodell. Eine Schleife über 100.000 Zeilen mit zwei Zellzugriffen pro Iteration erzeugt 200.000 COM-Round-trips. Liest man den gesamten Bereich vorab in ein Variant-Array, reduziert sich das auf genau 2 Aufrufe: einmal lesen, einmal schreiben.
Wer diesen Unterschied versteht, weiß wo er ansetzen muss. Die größten Gewinne entstehen durch weniger COM-Aufrufe und abgeschalteten Excel-Overhead – genau das adressieren die folgenden Tipps.
Die 10 wirksamsten VBA-Speed-Optimierungen
1. ScreenUpdating deaktivieren
Jede Zelländerung löst sofort eine Bildschirmaktualisierung aus. Mit ScreenUpdating = False überspringt Excel alle Zwischenschritte und aktualisiert die Anzeige erst am Ende.
Vorher:
Sub DatenFuellen() Dim i As Long For i = 1 To 10000 Cells(i, 1).Value = i * 2 Next i End Sub
Nachher:
Sub DatenFuellen() Application.ScreenUpdating = False Dim i As Long For i = 1 To 10000 Cells(i, 1).Value = i * 2 Next i Application.ScreenUpdating = True End Sub
Wirkung: 2–5-fache Beschleunigung bei Makros mit vielen Zelländerungen. Das Flackern verschwindet als sofortiger Bonus für den Anwender.
Wichtig: ScreenUpdating = True muss am Ende immer gesetzt werden – auch im Fehlerfall. Bleibt es auf False, wirkt Excel eingefroren.
2. Berechnung auf Manuell stellen
Nach jeder Zelländerung berechnet Excel alle abhängigen Formeln neu. In formellastigen Arbeitsmappen dominiert diese Neuberechnung die Laufzeit.
Vorher:
Sub PreiseAktualisieren() Dim i As Long For i = 2 To 5000 Cells(i, 3).Value = Cells(i, 1).Value * Cells(i, 2).Value Next i End Sub
Nachher:
Sub PreiseAktualisieren() Application.Calculation = xlCalculationManual Dim i As Long For i = 2 To 5000 Cells(i, 3).Value = Cells(i, 1).Value * Cells(i, 2).Value Next i Application.Calculation = xlCalculationAutomatic End Sub
Wann zurücksetzen: Immer am Ende auf xlCalculationAutomatic zurücksetzen. Wenn Formeln die Änderungen noch vor Ende des Makros berücksichtigen müssen, vorher einmalig Application.Calculate aufrufen. Nutzt die Arbeitsmappe xlCalculationSemiautomatic, den Ausgangszustand vorher speichern und wiederherstellen.
3. Arrays statt Zelle-für-Zelle-Schleifen verwenden
Dies ist die einzeln wirksamste Optimierung in VBA. Statt Zellen in einer Schleife einzeln zu lesen und zu schreiben, lädt man den gesamten Bereich in ein Variant-Array, verarbeitet ihn im Speicher und schreibt das Ergebnis in einem Schritt zurück.
Vorher:
Sub NamenBereinigen_Langsam() Dim i As Long For i = 2 To 50001 Cells(i, 2).Value = UCase(Trim(Cells(i, 1).Value)) Next i End Sub
Das erzeugt 100.000 COM-Round-trips (50.000 Lesezugriffe + 50.000 Schreibzugriffe).
Nachher:
Sub NamenBereinigen_Schnell() Dim src As Variant, dst As Variant, i As Long src = Range("A2:A50001").Value2 ' 1 COM-Lesezugriff ReDim dst(1 To UBound(src, 1), 1 To 1) For i = 1 To UBound(src, 1) dst(i, 1) = UCase(Trim(CStr(src(i, 1)))) Next i Range("B2").Resize(UBound(dst, 1), 1).Value2 = dst ' 1 COM-Schreibzugriff End Sub
Unabhängig von der Zeilenzahl werden genau 2 COM-Aufrufe ausgeführt.
Benchmark (50.000 Zeilen):
| Ansatz | Laufzeit |
|---|---|
| Zelle-für-Zelle-Schleife | ~45 Sekunden |
| Array-In/Out | ~0,4 Sekunden |
Faktor: ~112×. Der Array-Ansatz lohnt sich ab etwa 100 Zeilen. Darunter ist der Unterschied kaum spürbar.
Wichtige Regeln:
- Arrays aus
.Value2sind 1-basiert und zweidimensional:arr(Zeile, Spalte). - Das Ausgabe-Array vor der Schleife mit
ReDimauf die richtige Größe bringen. .Value2für Lesen und Schreiben verwenden (siehe Tipp Nr. 8).
4. Select und Activate vermeiden
Select und Activate zwingen Excel zu einer sichtbaren Fokusänderung – eine UI-Operation mit Overhead. Jeder darauffolgende Selection-Zugriff fügt eine weitere Indirektionsstufe hinzu. Direkte Objektreferenzen eliminieren beides.
Vorher:
Sub KopfzeileKopieren_Langsam() Sheets("Vorlage").Select Range("A1:F1").Select Selection.Copy Sheets("Bericht").Select Range("A1").Select ActiveSheet.Paste End Sub
Nachher:
Sub KopfzeileKopieren_Schnell() Sheets("Vorlage").Range("A1:F1").Copy _ Destination:=Sheets("Bericht").Range("A1") End Sub
Neben der Performance wird der Code deutlich lesbarer und bricht nicht, wenn sich der Tabellenfokus unerwartet ändert.
5. Events deaktivieren
Excel löst Worksheet_Change, Worksheet_Calculate und Worksheet_SelectionChange bei jeder Zelländerung aus. In Schleifen können diese Event-Handler hundert- oder tausendfach aufgerufen werden – und dabei jeweils weiteren VBA-Code ausführen.
Vorher:
Sub StatusSchreiben() Dim i As Long For i = 2 To 1001 Cells(i, 5).Value = "Verarbeitet" ' löst Worksheet_Change 1000x aus Next i End Sub
Nachher:
Sub StatusSchreiben() Application.EnableEvents = False Dim i As Long For i = 2 To 1001 Cells(i, 5).Value = "Verarbeitet" Next i Application.EnableEvents = True End Sub
Achtung: Wenn eigene Event-Handler auf Makro-Änderungen reagieren sollen (z. B. eine Validierungsroutine), Events nur um die Teile herum deaktivieren, die keine Events benötigen.
6. With...End With verwenden
Jeder Punkt in einer Objektkette (z. B. Workbooks("daten.xlsx").Sheets("Roh").Range("A1")) löst ein Zwischenobjekt auf. With...End With löst das übergeordnete Objekt einmalig auf und nutzt die interne Referenz für alle folgenden Zugriffe im Block.
Vorher:
Sub TabelleFormatieren_Langsam() Workbooks("daten.xlsx").Sheets("Roh").Range("A1:F1").Font.Bold = True Workbooks("daten.xlsx").Sheets("Roh").Range("A1:F1").Font.Size = 12 Workbooks("daten.xlsx").Sheets("Roh").Range("A1:F1").Interior.Color = RGB(200, 200, 255) Workbooks("daten.xlsx").Sheets("Roh").Range("A1:F1").HorizontalAlignment = xlCenter End Sub
Nachher:
Sub TabelleFormatieren_Schnell() With Workbooks("daten.xlsx").Sheets("Roh").Range("A1:F1") .Font.Bold = True .Font.Size = 12 .Interior.Color = RGB(200, 200, 255) .HorizontalAlignment = xlCenter End With End Sub
With-Blöcke lassen sich verschachteln und machen tief verschachtelte Objekthierarchien deutlich übersichtlicher.
7. Variablentypen explizit deklarieren
Eine ungetypte Dim x-Variable ist standardmäßig Variant – ein 16-Byte-Universalbehälter, der bei jeder Operation eine Typprüfung erfordert. Explizite Typen wie Long, String oder Double verwenden einfachere, schnellere interne Darstellungen.
Vorher:
Sub WerteSummieren_Langsam() Dim i, gesamt, wert For i = 1 To 100000 wert = Cells(i, 1).Value gesamt = gesamt + wert Next i End Sub
Nachher:
Sub WerteSummieren_Schnell() Dim i As Long, gesamt As Double, wert As Double For i = 1 To 100000 wert = Cells(i, 1).Value gesamt = gesamt + wert Next i End Sub
Option Explicit am Anfang jedes Moduls erzwingt die Deklaration aller Variablen und fängt Tippfehler bereits zur Kompilierzeit ab.
Für Schleifenzähler immer Long statt Integer verwenden. Auf modernem 32-Bit- und 64-Bit-Excel ist Long genauso schnell und läuft nicht bei 32.767 über.
8. Value2 statt Value verwenden
Die .Value-Eigenschaft konvertiert Datums-Seriennummern in Date-Objekte und Währungswerte in Currency-Typen – bei jedem Zugriff. .Value2 liefert den rohen Zahlenwert ohne diese Konvertierung.
Vorher:
arr = Range("A1:A10000").Value ' konvertiert Datums- und Währungszellen
Nachher:
arr = Range("A1:A10000").Value2 ' Rohwerte, keine Konvertierung
Wann es sich lohnt: Bei Bereichen mit Datums- oder Währungsformatierung. Bei reinen Texten und unformatierten Zahlen ist der Unterschied minimal. Beim Zurückschreiben von Arrays in Zellen ist .Value2 = arr gleichermaßen gültig und gleichermaßen schneller.
9. Copy/Paste vermeiden – direkte Wertübertragung
Range.Copy gefolgt von PasteSpecial aktiviert Zwischenablage-Operationen, Bildschirmaktualisierungen und Format-Übertragungslogik. Für reine Wertübertragungen ist die direkte Zuweisung schneller und klarer.
Vorher:
Range("A1:A5000").Copy Range("B1").PasteSpecial Paste:=xlPasteValues Application.CutCopyMode = False
Nachher:
Range("B1:B5000").Value2 = Range("A1:A5000").Value2
Eine Zeile. Keine Zwischenablage. Keine Formatierungsnebeneffekte.
10. For Each statt For i = 1 To n (bei Collections)
Beim Durchlaufen von Excel-Collection-Objekten (Worksheets, Charts, ListObjects) ruft ein indiziertes For i in jeder Iteration .Item(i) auf – ein zusätzlicher COM-Aufruf pro Schritt. For Each greift direkt auf den internen Objektzeiger zu.
Vorher:
Sub TabellenVerarbeiten_Langsam() Dim i As Long For i = 1 To Worksheets.Count Worksheets(i).Cells(1, 1).Value = "Überschrift" Next i End Sub
Nachher:
Sub TabellenVerarbeiten_Schnell() Dim ws As Worksheet For Each ws In Worksheets ws.Cells(1, 1).Value = "Überschrift" Next ws End Sub
Hinweis: Für numerische Schleifen über Variant-Arrays (wenn der Index benötigt wird) ist For i weiterhin richtig. For Each lohnt sich speziell bei Excel-Collection-Objekten.
Performance-Boilerplate – Copy-Paste-fertig
Dieses Template speichert alle Application-Zustände, schaltet alle Optimierungen ein und stellt die Zustände garantiert wieder her – auch bei einem Laufzeitfehler.
'================================================================= ' Performance-Wrapper ' SetPerformanceMode(True) vor dem eigentlichen Code aufrufen, ' SetPerformanceMode(False) danach – auch im Fehlerfall. '================================================================= Private Sub SetPerformanceMode(bAn As Boolean) Static altCalc As XlCalculation Static altScr As Boolean Static altEvt As Boolean Static altDisp As Boolean If bAn Then altCalc = Application.Calculation altScr = Application.ScreenUpdating altEvt = Application.EnableEvents altDisp = Application.DisplayAlerts Application.ScreenUpdating = False Application.Calculation = xlCalculationManual Application.EnableEvents = False Application.DisplayAlerts = False Else Application.DisplayAlerts = altDisp Application.EnableEvents = altEvt Application.Calculation = altCalc Application.ScreenUpdating = altScr End If End Sub '================================================================= ' Beispiel: Wrapper in einem beliebigen Sub verwenden '================================================================= Sub IhrMakroHier() On Error GoTo Aufraumen SetPerformanceMode True '-- Ihr Code hier ------------------------------------------ Dim src As Variant, dst As Variant, i As Long src = Sheets("Daten").Range("A2:C10001").Value2 ReDim dst(1 To UBound(src, 1), 1 To 1) For i = 1 To UBound(src, 1) dst(i, 1) = src(i, 1) * src(i, 2) + src(i, 3) Next i Sheets("Ergebnis").Range("A2").Resize(UBound(dst, 1), 1).Value2 = dst '-- Ende Ihr Code ------------------------------------------ Aufraumen: SetPerformanceMode False If Err.Number <> 0 Then MsgBox "Fehler " & Err.Number & ": " & Err.Description, vbCritical End If End Sub
Das Schlüsselwort Static erhält die Werte zwischen Aufrufen innerhalb derselben Excel-Sitzung. SetPerformanceMode False stellt damit immer genau den Zustand wieder her, der beim passenden SetPerformanceMode True gesichert wurde.
VBA-Performance messen
Nur optimieren, was man gemessen hat. Die eingebaute Timer-Funktion gibt die Sekunden seit Mitternacht als Double zurück – auf Windows genau auf 10–15 Millisekunden, ausreichend für Makro-Benchmarking.
Sub BenchmarkVorlage() Dim tStart As Double tStart = Timer '-- zu messender Code -- Dim i As Long For i = 1 To 100000 Cells(i, 1).Value = i Next i '-- Ende Code -- Debug.Print "Laufzeit: " & Format(Timer - tStart, "0.000") & " s" End Sub
Empfehlungen:
- Jede Version mindestens 3-mal laufen lassen und den Median nehmen (erster Lauf enthält Cold-Start-Overhead).
- Mit realistischen Datenmengen messen – Performance-Charakteristiken ändern sich mit der Skalierung.
- Ausgaben im Direktfenster lesen (
Strg+G), ohne das Makro zu unterbrechen. - Für Submillisekunden-Genauigkeit die Windows-API
QueryPerformanceCounterverwenden. Für typische Makro-Arbeit reichtTimer.
Häufige Fehler, die die Performance ruinieren
Volatile Formeln im Arbeitsblatt
Funktionen wie BEREICH.VERSCHIEBEN, INDIREKT, JETZT, HEUTE und ZUFALLSZAHL berechnen sich bei jeder Arbeitsblattänderung neu – nicht nur wenn ihre Eingaben sich ändern. Eine Arbeitsmappe mit Tausenden von BEREICH.VERSCHIEBEN-Aufrufen wird während jedes Makrolaufs stark neuberechnen, selbst teilweise unter xlCalculationManual.
Lösung: BEREICH.VERSCHIEBEN durch INDEX ersetzen, INDIREKT durch direkte oder strukturierte Tabellenverweise, JETZT()/HEUTE() aus Zellen entfernen, die keine Live-Aktualisierung brauchen.
Ganzspalten-Referenzen
' Vermeiden – verarbeitet 1.048.576 Zellen: Range("A:A").Copy Range("B:B")
Bereiche immer auf den tatsächlichen Datenbereich begrenzen:
Dim letzteZeile As Long letzteZeile = Cells(Rows.Count, 1).End(xlUp).Row Range("A1:A" & letzteZeile).Copy Range("B1")
Formatierungen in Schleifen
Formatierungsaufrufe innerhalb einer Zeilenschleife multiplizieren Format-Change-Events. Formatierungen am Ende zusammenfassen, nachdem die Daten geschrieben wurden:
' Langsam – 5.000 einzelne Formatierungsaufrufe: For i = 1 To 5000 Cells(i, 1).Value = i * 10 Cells(i, 1).Interior.Color = RGB(255, 255, 200) Next i ' Schnell – 1 Formatierungsaufruf auf den gesamten Bereich: For i = 1 To 5000 Cells(i, 1).Value = i * 10 Next i Range("A1:A5000").Interior.Color = RGB(255, 255, 200)
FAQ
Wie viel schneller ist VBA mit ScreenUpdating = False?
Typischerweise 2–10-mal schneller bei Makros mit vielen Zelländerungen. Der genaue Gewinn hängt von der Anzahl der Änderungen und der Komplexität des sichtbaren Arbeitsblatts ab. Kombiniert mit xlCalculationManual und EnableEvents = False ist der Gesamteffekt in formellastigen Arbeitsmappen oft 10–50-fach.
Sollte ich immer Arrays statt Schleifen verwenden?
Bei Bereichen über ~100 Zeilen: ja, fast immer. Darunter kann der Overhead durch Array-Allokation und Indexzugriffe den Vorteil der reduzierten COM-Aufrufe aufwiegen. Der praktische Schwellenwert liegt je nach Verarbeitungslogik bei etwa 50–200 Zeilen.
Verbessert Option Explicit die Performance?
Marginal – es erzwingt typisierte Deklarationen und reduziert Variant-Nutzung. Der wichtigere Vorteil ist Korrektheit: Option Explicit fängt Tippfehler bereits zur Kompilierzeit ab statt als stille Laufzeitfehler. Immer aktivieren.
Wie schreibt man Daten am schnellsten in Zellen?
Direkte Array-Zuweisung: Range("A1:A10000").Value2 = meinArray. Das ist ein einziger COM-Schreibzugriff, unabhängig von der Array-Größe. .Copy/.Paste, Zelle-für-Zelle-Schleifen und Union-Bereiche in Schreibschleifen vermeiden.
Kann VBA mehrere CPU-Kerne nutzen?
Nein. VBA ist single-threaded und läuft im Hauptthread von Excel. Excels eigene Multi-Threaded-Berechnung (MTR) nutzt mehrere Kerne, aber nur für die Formelberechnung – nicht für VBA-Code. Wer Parallelverarbeitung für datenlastige Aufgaben benötigt, sollte diese als Arbeitsblattformeln oder als Power-Query-Transformationen strukturieren.
Wann sollte man nicht optimieren?
Wenn das Makro unter 2 Sekunden läuft und selten ausgeführt wird, bringt Optimierung nur Komplexität ohne messbaren Nutzen für den Anwender. Erst korrekten, klaren Code schreiben – dann gezielt die Hotspots optimieren, die man tatsächlich gemessen hat.
VBA-Code automatisch optimieren lassen
Der VBA Optimizer analysiert bestehende Makros und schlägt konkrete Verbesserungen automatisch vor – ScreenUpdating-Toggles, Array-Umbauten, Select-Eliminierung und mehr.
Kostenlos testen – 10 Anfragen inklusive, keine Registrierung erforderlich.
Code-Generierung
Automatische VBA-Code-Erstellung durch natürliche Sprache
Code-Analyse
Code verstehen und dokumentieren
Debugging
Intelligente Fehlerfindung und -behebung
Code-Verbesserung
Optimierung und Fehlerbehandlung
UI-Design
UserForms über natürliche Sprache erzeugen
Snippet-Verwaltung
Über 100 vorgefertigte Code-Bausteine
Weitere Artikel
KI optimiert Ihren VBA-Code automatisch
Der VBA Optimizer analysiert bestehende Makros und schlägt ScreenUpdating-Toggles, Array-Umbauten und Select-Eliminierung vor – in Sekunden.
