Vb.net, использующий List (Of String) с течением времени в следующей процедуре, замедляется - PullRequest
0 голосов
/ 01 июля 2018

РЕДАКТИРОВАТЬ: я поставил полные 2 сабвуфера и 1 функцию, которые используются. Я также разместил комментарии вокруг раздела, который со временем замедляется.

Прежде чем кто-либо упоминает FileHelpers, я не хочу использовать сторонний компонент.

Итак, у меня есть файл CSV, 3,5 миллиона строк, и я анализирую каждую строку CSV и вставляю ее в SQLite (неиндексную таблицу), чтобы ускорить работу. Я также буферирую 100 000 строк за раз из CSV - все хорошо с миром, но в моем цикле (после того, как я буферизовал 100 000 строк в список строк, следующий код проходит и разбивает каждую строку, соответственно, наращивается вставка строки и все готово - около 1000 строк обрабатывается в секунду - я могу с этим смириться, но после пары сотен тысяч строк она начинает замедляться до примерно 200 строк в секунду. В конце концов, после примерно 600 000 строк, это ползет до 25 строк и далее (не знаю, в какой момент), после того, как более 2 миллионов строк замедляются до 10-15 строк. Мне бы очень хотелось сохранять 1000 строк в секунду (или даже улучшать их, если это возможно).

Я пропустил некоторый код, подпрограмму fixsquote, которая вызывается для сортировки любых кавычек, а также оператор IF THEN, чтобы определить, есть ли кавычки, и разделить его по-разному - для этой конкретной строки с 3,5 миллионами CSV, есть кавычек нет, поэтому я просто хотел уменьшить количество кода, который я публикую здесь, чтобы было удобнее читать.

Первоначально я использовал древний массив для управления разбиением, и это было то же самое, замедление после X тысяч строк, но, глядя в Интернете, казалось, что список строк будет лучше, поэтому я преобразовал несколько строк кода для использования списка строк в моем цикле, и это не имеет значения. У меня есть чувство, хотя я, кажется, не использую массивы, я сбит с толку возможным замедлением. Что-то или не используется повторно должным образом и может быть проблема стека или кучи, о которых я не знаю?

У меня нет замедления во время массовой вставки - я могу полностью удалить вставку и выполнение в SQLITE, поэтому SQLITE не является проблемой. Это как-то связано со списком или строкой. Я собираюсь попробовать предложение о повышении версии .net - у меня было это на 4.6, так что подниму до 4.7.1 ******

Вот код, может кто-то заметит что-то очевидное.

  Private Sub CSVImport

    Dim SQLStr As New Text.StringBuilder
    Dim BigSQLStr As New Text.StringBuilder
    Dim Comma As String = ""
    Dim FirstInsertStr As New Text.StringBuilder
    Dim BufferT As Integer = 0

    Try


        If IO.File.Exists(FileName) = True Then


            Dim C As Integer
            Dim line As String



            FirstInsertStr.Clear()
            FirstInsertStr.Capacity = 0
            FirstInsertStr.Append("INSERT INTO " & Chr(34) & DestinationTableName & Chr(34))
            FirstInsertStr.Append(" (" & Chr(34) & "LON" & Chr(34))
            FirstInsertStr.Append(", " & Chr(34) & "LAT" & Chr(34))
            FirstInsertStr.Append(", " & Chr(34) & "NUMBER" & Chr(34))
            FirstInsertStr.Append(", " & Chr(34) & "STREET" & Chr(34))
            FirstInsertStr.Append(", " & Chr(34) & "UNIT" & Chr(34))
            FirstInsertStr.Append(", " & Chr(34) & "CITY" & Chr(34))
            FirstInsertStr.Append(", " & Chr(34) & "DISTRICT" & Chr(34))
            FirstInsertStr.Append(", " & Chr(34) & "REGION" & Chr(34))
            FirstInsertStr.Append(", " & Chr(34) & "POSTCODE" & Chr(34))
            FirstInsertStr.Append(", " & Chr(34) & "ADDRESSIO_ID" & Chr(34))
            FirstInsertStr.Append(", " & Chr(34) & "HASH" & Chr(34))
            FirstInsertStr.Append(") VALUES (")



            BufferT = 0

            BigSQLStr.Clear()
            BigSQLStr.Capacity = 0

            ' Create new StreamReader instance with Using block.
            Using reader As IO.StreamReader = New IO.StreamReader(FileName)
                ' Read one line from file
                line = reader.ReadLine ' first line in headers so ignore

                Do Until line = Nothing

                    ' Stop
                    Dim BufferRead As New List(Of String)
                    Dim BufferLoad As Integer = 0

                    Do Until BufferLoad = 100000
                        line = reader.ReadLine
                        If line = Nothing Then
                            Exit Do
                        End If

                        BufferRead.Add(line)
                        BufferLoad += 1
                    Loop

                    Dim Z As Integer = 0

                    For Z = 0 To BufferLoad - 1

                        BufferT += 1

                        Comma = ""
     ' ************** I BELIEVE THE SLOWDOWN IS WITHIN HERE, FAST AT FIRST, THEN SLOWS DOWN AFTER 30k RECORDS OR SO **********                           
                        Dim objFields2 As New List(Of String)

                        If BufferRead(Z).Contains(Chr(34)) = True Then
                            BufferRead(Z) = FixsQuote(BufferRead(Z))

                            objFields2.AddRange(Split(BufferRead(Z), ",", Chr(34), True))
                        Else
                            objFields2.AddRange(BufferRead(Z).Split(","))

                        End If


                        With SQLStr
                            .Clear()
                            .Capacity = 0
                            For C = 0 To objFields2.Count - 1
                                If C > 11 Then
                                    Exit For ' we only ever want the first 11 fields.
                                End If
                                If C > 0 Then
                                    Comma = ","
                                End If
                                If IsDBNull(objFields2(C)) = False Then
                                    If objFields2(C).Contains(Chr(34)) = True Then
                                        If objFields2(C).Replace(Chr(34), "").Length > 0 Then
                                            .Append(Comma & "'" & FixsQuote(objFields2(C).Replace(Chr(34), "")) & "'")
                                        Else
                                            .Append(Comma & "Null")
                                        End If
                                    Else
                                        If objFields2(C).Length > 0 Then
                                            .Append(Comma & "'" & FixsQuote(objFields2(C)) & "'")
                                        Else
                                            .Append(Comma & "Null")
                                        End If
                                    End If
                                Else
                                    .Append(Comma & "Null")
                                End If
                            Next

                            BigSQLStr.Append(FirstInsertStr.ToString)
                            BigSQLStr.Append(.ToString)
                            BigSQLStr.Append(");")

                            ' Now Insert what we have
                            If BufferT = 1000 Then

                                Using OleCMD As New SQLite.SQLiteCommand(BigSQLStr.ToString, AddressesIOSQLDB)
                                    OleCMD.CommandTimeout = 0
                                    OleCMD.ExecuteNonQuery()
                                End Using

                                BigSQLStr.Clear()
                                BigSQLStr.Capacity = 0
                                BufferT = 0
                            End If


                        End With


                        objFields2.Clear()
                        objFields2 = Nothing
                    Next

                    BufferRead.Clear()

    ' ****************** end of what i believe is the slow down ****************

                Loop
                If BufferT > 0 Then
                    Try
                        Using OleCMD As New SQLite.SQLiteCommand(BigSQLStr.ToString, AddressesIOSQLDB)
                            OleCMD.CommandTimeout = 0
                            OleCMD.ExecuteNonQuery()
                        End Using
                    Catch ex As Exception
                        Stop
                    End Try
                    BufferT = 0
                End If
            End Using
        End If


    Catch ex As Exception
        Stop
    End Try


    BigSQLStr.Clear()
    BigSQLStr.Capacity = 0

    FirstInsertStr.Clear()
    FirstInsertStr.Capacity = 0

    SQLStr.Clear()
    SQLStr.Capacity = 0

   End Sub



Private Function FixsQuote(ByVal s As String) As String
    Return s.Replace("'", "''")
End Function



Private Function Split(
    ByVal expression As String,
    ByVal delimiter As String,
    ByVal qualifier As String,
    ByVal ignoreCase As Boolean) As List(Of String)
    Dim _Statement As String = String.Format("{0}(?=(?:[^{1}]*{1}[^{1}]*{1})*(?![^{1}]*{1}))", Regex.Escape(delimiter), Regex.Escape(qualifier))

    Dim _Options As RegexOptions = RegexOptions.Compiled Or RegexOptions.Multiline
    If ignoreCase Then _Options = _Options Or RegexOptions.IgnoreCase

        Dim _Expression As Regex = New Regex(_Statement, _Options)
        Return _Expression.Split(expression).ToList
End Function

1 Ответ

0 голосов
/ 01 июля 2018

Я не могу в это поверить, но, посмотрев на предложение TnTinMn (которое сделало переход менее чем за секунду для реальных переходов, а также удалил текстовые строители строк BigSQLStr и SQLStr (потому что теперь я выполняю каждую вставку внутри транзакции, я накачка 20 000 записей в секунду. Сдули парней, сдули. Спасибо всем большое за то, что заставили меня разобрать мой код и не думали, что компоновщик строк является решением для всего. С этого момента я вкладываю свои большие вставки в транзакции и оставляю когда я могу это сделать, это намного быстрее, теперь я не могу в это поверить (и я проверил это по количеству записей в базе данных).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...