VBA 코딩 패턴: Range Loop-쓰기(Write)

VBA 코딩 패턴 중Range Loop-쓰기(Write) 에 대한 내용을 살펴본다.

요약

Variant Array를 이용하여 엑셀 시트에서 많은 데이터를 쓸때 성능을 개선하는 코딩 패턴에 대해 소개한다.

엑셀 시트에서 많은 데이터를 읽을 때 성능을 개선하는 방법은 아래 글에서 이미 다루었다.

VBA 코딩 패턴: Range Loop-읽기(Read)

한줄 요약: Loop 보다 Variant Array로 읽는 방법이 비교할 수 없을 정도로 빠름.

이번에는 반대로 VBA 코드내에서 데이터를 엑셀 시트로 쓰는 방법에 대해 살펴본다. 각 방법에 대한 성능은 가장 아래쪽에 비교해 놓았다.

테스트 방법

다음과 같은 방법으로 테스트를 실행한다.

  • Range(“B2”)를 기준으로 행과 열을 증가시키면서 각 cell에 값을 쓴다.
  • cell에 기록되는 값은 “행번호, 열번호” 형식으로 한다.
  • 열의 수는 고정(10열)으로 둔다.
  • 행의 수는 10, 100, 500, 1000 ~ 100,000 까지 증가시키면서 반복한다.
  • cell의 수는 행의 수 x 10 이므로, 최대 1백만 cell까지 테스트한다.

테스트 코드는 github에서 다운로드받을 수 있다.

github 파일 Link: DAToolset/VBACode/VBA 코딩 패턴-Range Loop(Write).xlsm

기본 패턴-Nested Loop

기본 패턴은 시작 Cell을 기준으로 행과 열의 Offset을 증가시키면서 행에 대한 Loop(반복) 구문 내부에 열에 대한 Loop(반복) 구문을 사용한다.

상세 코드는 다음과 같다. 이전 글 VBA 코딩 패턴: Range Loop-읽기(Read) 와 코드가 거의 동일하고, 읽기를 쓰기로 변경하였다. (10 행)

Range개체의 Text는 Read-only 속성이라서 값을 입력할 수 없어서 Range 개체에 값을 바로 입력했다.

'* 기본 패턴(Nested Loop)
Public Sub NestedLoop(aTargetRowCnt As Long)
    Dim lRowOffset As Long, lColOffset As Long, oBaseRange As Range, sVal As String
    Set oBaseRange = Range("B2")
    lRowOffset = 0
    For lRowOffset = 0 To aTargetRowCnt '읽는 행의 수(충분히 큰 수로 지정하고 Loop 내부에서 종료 조건 설정)
        If Trim(oBaseRange.Offset(lRowOffset, 0).Value) = "" Then Exit For 'Loop 종료 조건
        For lColOffset = 0 To 9 '읽는 열의 수
            'cell 단위로 쓰는 코드 예시
            oBaseRange.Offset(lRowOffset, lColOffset) = CStr(lRowOffset + 1) + "," + CStr(lColOffset + 1)
        Next lColOffset
    Next lRowOffset
End Sub

단일 Loop를 사용하는 패턴-Single Loop(1)

열에 대한 내부 반복문을 열 개수만큼의 문장(11행 ~ 20행)으로 대체하였다. 마찬가지로 Range 개체에 값을 바로 입력했다.

'단일 Loop를 사용하는 패턴: Single Loop(1)
Public Sub SingleLoop1(aTargetRowCnt As Long)
    Dim lRowOffset As Long, lColOffset As Long, oBaseRange As Range, sVal As String
    Set oBaseRange = Range("B2")
    lRowOffset = 0
    Dim lRowCount As Long, lColCount As Long, oHeaderRange As Range
    Set oHeaderRange = oBaseRange.Offset(-1, 0)
    lRowCount = aTargetRowCnt
    lColCount = Range(oHeaderRange, oHeaderRange.End(xlToRight)).Columns.Count
    For lRowOffset = 0 To lRowCount - 1 '읽는 행의 수 (Offset은 0부터 시작하므로 Count -1 까지 Loop 실행해야 함)
        oBaseRange.Offset(lRowOffset, 0) = CStr(lRowOffset + 1) + ",1"
        oBaseRange.Offset(lRowOffset, 1) = CStr(lRowOffset + 1) + ",2"
        oBaseRange.Offset(lRowOffset, 2) = CStr(lRowOffset + 1) + ",3"
        oBaseRange.Offset(lRowOffset, 3) = CStr(lRowOffset + 1) + ",4"
        oBaseRange.Offset(lRowOffset, 4) = CStr(lRowOffset + 1) + ",5"
        oBaseRange.Offset(lRowOffset, 5) = CStr(lRowOffset + 1) + ",6"
        oBaseRange.Offset(lRowOffset, 6) = CStr(lRowOffset + 1) + ",7"
        oBaseRange.Offset(lRowOffset, 7) = CStr(lRowOffset + 1) + ",8"
        oBaseRange.Offset(lRowOffset, 8) = CStr(lRowOffset + 1) + ",9"
        oBaseRange.Offset(lRowOffset, 9) = CStr(lRowOffset + 1) + ",10"
    Next lRowOffset
End Sub

단일 Loop를 사용하는 패턴-Single Loop(2)

Single Loop 구문을 유지하고 Range에 값을 입력할 때 Value2를 사용하였다.

'단일 Loop를 사용하는 패턴: Single Loop(2), Value2 사용
Public Sub SingleLoop2(aTargetRowCnt As Long)
    Dim lRowOffset As Long, lColOffset As Long, oBaseRange As Range, sVal As String
    Set oBaseRange = Range("B2")
    lRowOffset = 0
    Dim lRowCount As Long, lColCount As Long, oHeaderRange As Range
    Set oHeaderRange = oBaseRange.Offset(-1, 0)
    lRowCount = aTargetRowCnt
    lColCount = Range(oHeaderRange, oHeaderRange.End(xlToRight)).Columns.Count
    For lRowOffset = 0 To lRowCount - 1 '읽는 행의 수 (Offset은 0부터 시작하므로 Count -1 까지 Loop 실행해야 함)
        oBaseRange.Offset(lRowOffset, 0).Value2 = CStr(lRowOffset + 1) + ",1"
        oBaseRange.Offset(lRowOffset, 1).Value2 = CStr(lRowOffset + 1) + ",2"
        oBaseRange.Offset(lRowOffset, 2).Value2 = CStr(lRowOffset + 1) + ",3"
        oBaseRange.Offset(lRowOffset, 3).Value2 = CStr(lRowOffset + 1) + ",4"
        oBaseRange.Offset(lRowOffset, 4).Value2 = CStr(lRowOffset + 1) + ",5"
        oBaseRange.Offset(lRowOffset, 5).Value2 = CStr(lRowOffset + 1) + ",6"
        oBaseRange.Offset(lRowOffset, 6).Value2 = CStr(lRowOffset + 1) + ",7"
        oBaseRange.Offset(lRowOffset, 7).Value2 = CStr(lRowOffset + 1) + ",8"
        oBaseRange.Offset(lRowOffset, 8).Value2 = CStr(lRowOffset + 1) + ",9"
        oBaseRange.Offset(lRowOffset, 9).Value2 = CStr(lRowOffset + 1) + ",10"
    Next lRowOffset
End Sub

memory에 있는 데이터를 한번에 Range에 쓰는 방법-Variant Array 사용

2차원 Variant Array에 저장된 데이터를 한번에 Range에 쓰는 방법으로 상세 코드는 다음과 같다.

' 한번에 쓰는 패턴
Public Sub VariantArray(aTargetRowCnt As Long)
    Dim vRngArr As Variant, oBaseRange As Range, oHeaderRange As Range, lColCount As Long, sVal As String
    Set oBaseRange = Range("B2")
    Set oHeaderRange = oBaseRange.Offset(-1, 0)
    lColCount = Range(oHeaderRange, oHeaderRange.End(xlToRight)).Columns.Count
    ReDim vRngArr(1 To aTargetRowCnt, 1 To lColCount)
    Dim lCol As Long, lRow As Long
    For lRow = LBound(vRngArr, 1) To UBound(vRngArr, 1)
        For lCol = LBound(vRngArr, 2) To UBound(vRngArr, 2)
            vRngArr(lRow, lCol) = CStr(lRow) + "," + CStr(lCol)
        Next lCol
    Next lRow
    oBaseRange.Resize(aTargetRowCnt, lColCount).Value2 = vRngArr
End Sub

핵심 코드는 14행이다. Range에 Variant array를 바로 대입하여 데이터를 write한다.

    oBaseRange.Resize(aTargetRowCnt, lColCount).Value2 = vRngArr

VBA 코딩 패턴: Range Loop-읽기(Read) 에서 read하는 다음 코드와 비교해 보면 좌항과 우항만 바뀌고 동일한 것을 알 수 있다.

    vRngArr = oBaseRange.Resize(aTargetRowCnt, lColCount).Value2

성능비교

Range 쓰기 성능 비교

VBA 코딩 패턴: Range Loop-읽기(Read) 와 동일하게 10부터 10만까지 Row Count를 증가시키면서 각 방법의 실행시간을 정리하였다.
(실행환경: 2019 LG Gram, CPU i5, Ram 16GB, 256GB SSD)

표1. Row Count, Cell Count와 각 방법의 쓰기(Write) 소요시간(초)
표1. Row Count, Cell Count와 각 방법의 쓰기(Write) 소요시간(초)
그림1. Row Count, Cell Count와 각 방법의 쓰기(Write) 소요시간(초)
그림1. Row Count, Cell Count와 각 방법의 쓰기(Write) 소요시간(초)

Range 쓰기 성능은 다음과 같이 요약할 수 있다.

  • Value2를 이용하여 값을 입력하는 방법과 Range 개체에 값을 바로 입력하는 방법은 거의 차이가 없다.
  • Single Loop보다 Nested Loop가 약간 더 빠른 결과가 나왔다. 이건 읽기(Read)와 반대라서 항상 더 빠르다고 하기 어려워 보인다.
  • Variant Array를 이용하여 데이터를 쓰면 비교할 수 없을 정도(최소 10배 이상)로 빠르다.

Range 읽기 성능 비교

읽기와 쓰기 성능을 바로 비교해 볼 수 있도록 읽기 성능 비교 내용을 아래에 복사해 둔다.

표2. Row Count, Cell Count와 각 방법의 읽기(Read) 소요시간(초)
표2. Row Count, Cell Count와 각 방법의 읽기(Read) 소요시간(초)
그림1. Row Count, Cell Count와 각 방법의 읽기(Read) 소요시간(초)
그림1. Row Count, Cell Count와 각 방법의 읽기(Read) 소요시간(초)

엑셀 시트(Sheet)에서 많은 양의 데이터를 VBA 코드로 읽고 쓸 때 Variant array를 활용하면 성능을 크게 향상시킬 수 있으니, 필요한 경우에 적절히 잘 활용하기 바란다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

ko_KR한국어