Long-running Process with Responsive Form - Performance Improvements
So, I'm working on a library for my internal applications that interacts with our PostgreSQL database (amongst many other things). One requirement at the moment is that this library can dump data from the database to a file. I have something working, but I've been trying to improve its performance as much as possible. This is what I'm currently looking at:
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
    With COPYReader
        Dim stopWatch As New Stopwatch
        Dim ts As TimeSpan
        Dim elapsedTime As String
        ' ** FIRST ATTEMPT
        stopWatch.Start()
        Dim BufferText As String = .ReadLine
        Do While Not BufferText Is Nothing
            CurrentPosition += 1
            OutputFile.WriteLine(BufferText)
            If Not UpdateForm Is Nothing Then
                UpdateForm.UpdateProgress(Convert.ToInt32((CurrentPosition / MaxRecords) * 100))
            End If
            BufferText = .ReadLine
        Loop
        OutputFile.Flush()
        OutputFile.Close()
        stopWatch.Stop()
        ts = stopWatch.Elapsed
        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
        ' ** FIRST ATTEMPT RESULTS
        ' ** Records Retrieved: 65358
        ' ** Time To Complete: 2:12.07
        ' ** Lines Written: 65358
        ' ** File Size: 8,166 KB
        ' ** SECOND ATTEMPT
        stopWatch.Start()
        Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
            TestOutputFile.Write(.ReadToEndAsync.Result)
        End Using
        stopWatch.Stop()
        ts = stopWatch.Elapsed
        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
        ' ** SECOND ATTEMPT
        ' ** Records Retrieved: 65358
        ' ** Time To Complete: 1:04.01
        ' ** Lines Written: 65358
        ' ** File Size: 8,102 KB
    End With
End Using
I've run multiple tests of each method and come up with pretty much the same results. FIRST ATTEMPT takes about twice as long as SECOND ATTEMPT
Obviously, the UpdateForm.UpdateProgress method used in FIRST ATTEMPT (used to keep the form responsive and show the current progress of the export) is going to cause the process to take longer because of the form updating and such involved, not to mention the line-by-line writing to the file. This is pretty much exactly why I was looking at reducing the number of extra calls by doing a full dump in one line of code.  The problem is that if I use the "one-liner", the form is completely unresponsive until the process is complete.
I've tried moving the code for the "all-in-one-shot" dump from the SECOND ATTEMPT into a separate Async method, but I'm extremely unfamiliar with async methods in general, so I'm (obviously) not doing it right:
Private Async Sub OutputToFile(ByVal COPYReader As NpgsqlCopyTextReader, ByVal DestinationFile As IO.FileInfo)
    ' ** METHOD 3
    Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
        Await TestOutputFile.WriteAsync(COPYReader.ReadToEndAsync.Result)
    End Using
    ' ** METHOD 3 RESULTS
    ' ** Records Retrieved: 65358
    ' ** Time To Complete: 0:15.07
    ' ** Lines Written: 34
    ' ** File Size: 4 KB
End Sub
One other thing to mention: I tried moving all of this to a BackgroundWorker, but I was getting some strange behavior when I tried to call my UpdateForm.UpdateProgress method which caused the application to completely skip the actual dumping process.  I've currently given up on trying to get this onto a separate thread, but I'm still open to other suggestions.  This is actually one of the smaller tables that I'm dumping, so I'm not looking forward to what one of the bigger ones will do.
Just for the sake of completeness, here's the UpdateForm class that I've implemented in my library for reusability across other applications:
Imports System.Windows.Forms
Namespace Common
    Public Class FormHandler
        Implements IDisposable
        Public Property ApplicationForm As Form
        Public Property ApplicationStatusLabel As Label
        Public Property ApplicationToolStripLabel As ToolStripStatusLabel
        Public Property ApplicationProgressBar As ProgressBar
        Private LabelVisibleState As Boolean = True
        Private ProgressBarVisibleState As Boolean = True
        Private CurrentStatusText As String
        Private CurrentProgress As Integer
        Public Sub New(ByVal AppForm As Form)
            ApplicationForm = AppForm
        End Sub
        Public Sub New(ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
            ApplicationStatusLabel = StatusLabel
            ApplicationToolStripLabel = Nothing
            ApplicationProgressBar = Progress
            ApplicationForm = ApplicationProgressBar.Parent.FindForm
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationStatusLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Public Sub New(ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
            ApplicationToolStripLabel = StatusLabel
            ApplicationStatusLabel = Nothing
            ApplicationProgressBar = Progress
            ApplicationForm = ApplicationProgressBar.Parent.FindForm
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationToolStripLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
            ApplicationForm = AppForm
            ApplicationStatusLabel = StatusLabel
            ApplicationToolStripLabel = Nothing
            ApplicationProgressBar = Progress
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationStatusLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
            ApplicationForm = AppForm
            ApplicationToolStripLabel = StatusLabel
            ApplicationStatusLabel = Nothing
            ApplicationProgressBar = Progress
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationToolStripLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Friend Sub UpdateProgress(ByVal StatusText As String, ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
            CurrentStatusText = StatusText
            CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal StatusText As String, ByVal PercentComplete As Decimal)
            CurrentStatusText = StatusText
            CurrentProgress = Convert.ToInt32(PercentComplete)
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal StatusText As String)
            CurrentStatusText = StatusText
            CurrentProgress = 0
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal PercentComplete As Decimal)
            CurrentProgress = Convert.ToInt32(PercentComplete)
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
            CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
            UpdateStatus()
        End Sub
        Friend Sub ResetProgressUpdate()
            CurrentStatusText = ""
            CurrentProgress = 0
            UpdateStatus()
        End Sub
        Private Sub UpdateStatus()
            If Not ApplicationForm Is Nothing Then
                If ApplicationForm.InvokeRequired Then
                    Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
                    Try
                        ApplicationForm.Invoke(UpdateInvoker)
                    Catch ex As Exception
                        Dim InvokeError As New ErrorHandler(ex)
                        InvokeError.LogException()
                    End Try
                Else
                    UpdateApplicationProgress(CurrentStatusText)
                End If
            End If
        End Sub
        Friend Sub UpdateApplicationProgress(ByVal ProgressText As String)
            If Not ApplicationForm Is Nothing Then
                With ApplicationForm
                    If Not ProgressText Is Nothing Then
                        If Not ApplicationStatusLabel Is Nothing Then
                            ApplicationStatusLabel.Text = ProgressText
                        End If
                        If Not ApplicationToolStripLabel Is Nothing Then
                            ApplicationToolStripLabel.Text = ProgressText
                        End If
                    End If
                    If Not ApplicationProgressBar Is Nothing Then
                        ApplicationProgressBar.Value = CurrentProgress
                    End If
                End With
                ApplicationForm.Refresh()
                Application.DoEvents()
            End If
        End Sub
        Public Sub Dispose() Implements IDisposable.Dispose
            If Not ApplicationForm Is Nothing Then
                ApplicationForm.Dispose()
            End If
            If Not ApplicationStatusLabel Is Nothing Then
                ApplicationStatusLabel.Visible = LabelVisibleState
                ApplicationStatusLabel.Dispose()
            End If
            If Not ApplicationToolStripLabel Is Nothing Then
                ApplicationToolStripLabel.Visible = LabelVisibleState
                ApplicationToolStripLabel.Dispose()
            End If
            If Not ApplicationProgressBar Is Nothing Then
                ApplicationProgressBar.Visible = ProgressBarVisibleState
                ApplicationProgressBar.Dispose()
            End If
        End Sub
    End Class
End Namespace
EDIT
Per the suggestion in the comments from @the_lotus, I modified my FIRST ATTEMPT slightly to check the value of the current progress (I declared a CurrentProgress variable as an Integer), and it dramatically improved the time taken:
' ** FOURTH ATTEMPT
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
    With COPYReader
        Dim stopWatch As New Stopwatch
        Dim ts As TimeSpan
        Dim elapsedTime As String
        Dim CurrentProgress As Integer = 0
        stopWatch.Start()
        Dim BufferText As String = .ReadLine
        Do While Not BufferText Is Nothing
            CurrentPosition += 1
            OutputFile.WriteLine(BufferText)
            ' ** Checks to see if the value of the ProgressBar will actually
            ' ** be changed by the CurrentPosition before making a call to
            ' ** UpdateProgress.  If the value doesn't change, don't waste
            ' ** the call
            If Convert.ToInt32((CurrentPosition / MaxRecords) * 100) <> CurrentProgress Then
                CurrentProgress = Convert.ToInt32((CurrentPosition / MaxRecords) * 100)
                If Not UpdateForm Is Nothing Then
                    UpdateForm.UpdateProgress(CurrentProgress)
                End If
            End If
            BufferText = .ReadLine
        Loop
        OutputFile.Flush()
        OutputFile.Close()
        stopWatch.Stop()
        ts = stopWatch.Elapsed
        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
    End With
End Using
' ** FOURTH ATTEMPT RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 0:47.45
' ** Lines Written: 65358
' ** File Size: 8,166 KB
Of course, the form is a "little" less responsive than if I were making the call on every record, but I'm thinking it's worth the trade-off.
EDIT #2
So that I could minimize the amount of code I'd have to retype (read: "copy/paste") each time I use the UpdateProgress method, I've moved the test for changed values over there and it seems to be operating with the same performance improvements.  Again, for the sake of completeness, here's the code for the two private methods involved in doing the actual progress/status update:
Private Sub UpdateStatus()
    If Not ApplicationForm Is Nothing Then
        If ApplicationForm.InvokeRequired Then
            Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
            Try
                ApplicationForm.Invoke(UpdateInvoker)
            Catch ex As Exception
                Dim InvokeError As New ErrorHandler(ex)
                InvokeError.LogException()
            End Try
        Else
            UpdateApplicationProgress()
        End If
    End If
End Sub
Private Sub UpdateApplicationProgress()
    Dim Changed As Boolean = False
    If Not ApplicationForm Is Nothing Then
        With ApplicationForm
            If Not CurrentStatusText Is Nothing Then
                If Not ApplicationStatusLabel Is Nothing Then
                    If ApplicationStatusLabel.Text <> CurrentStatusText Then
                        Changed = True
                        ApplicationStatusLabel.Text = CurrentStatusText
                    End If
                End If
                If Not ApplicationToolStripLabel Is Nothing Then
                    If ApplicationToolStripLabel.Text <> CurrentStatusText Then
                        Changed = True
                        ApplicationToolStripLabel.Text = CurrentStatusText
                    End If
                End If
            End If
            If Not ApplicationProgressBar Is Nothing Then
                If ApplicationProgressBar.Value <> CurrentProgress Then
                    Changed = True
                    ApplicationProgressBar.Value = CurrentProgress
                End If
            End If
        End With
        If Changed Then
            ApplicationForm.Refresh()
        End If
        Application.DoEvents()
    End If
End Sub
Doing it this way also came with the added benefit of returning some of the responsiveness to the form that was previously lost. I hope at least some of this code and information is helpful to someone out there.
vb.net npgsql
add a comment |
So, I'm working on a library for my internal applications that interacts with our PostgreSQL database (amongst many other things). One requirement at the moment is that this library can dump data from the database to a file. I have something working, but I've been trying to improve its performance as much as possible. This is what I'm currently looking at:
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
    With COPYReader
        Dim stopWatch As New Stopwatch
        Dim ts As TimeSpan
        Dim elapsedTime As String
        ' ** FIRST ATTEMPT
        stopWatch.Start()
        Dim BufferText As String = .ReadLine
        Do While Not BufferText Is Nothing
            CurrentPosition += 1
            OutputFile.WriteLine(BufferText)
            If Not UpdateForm Is Nothing Then
                UpdateForm.UpdateProgress(Convert.ToInt32((CurrentPosition / MaxRecords) * 100))
            End If
            BufferText = .ReadLine
        Loop
        OutputFile.Flush()
        OutputFile.Close()
        stopWatch.Stop()
        ts = stopWatch.Elapsed
        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
        ' ** FIRST ATTEMPT RESULTS
        ' ** Records Retrieved: 65358
        ' ** Time To Complete: 2:12.07
        ' ** Lines Written: 65358
        ' ** File Size: 8,166 KB
        ' ** SECOND ATTEMPT
        stopWatch.Start()
        Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
            TestOutputFile.Write(.ReadToEndAsync.Result)
        End Using
        stopWatch.Stop()
        ts = stopWatch.Elapsed
        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
        ' ** SECOND ATTEMPT
        ' ** Records Retrieved: 65358
        ' ** Time To Complete: 1:04.01
        ' ** Lines Written: 65358
        ' ** File Size: 8,102 KB
    End With
End Using
I've run multiple tests of each method and come up with pretty much the same results. FIRST ATTEMPT takes about twice as long as SECOND ATTEMPT
Obviously, the UpdateForm.UpdateProgress method used in FIRST ATTEMPT (used to keep the form responsive and show the current progress of the export) is going to cause the process to take longer because of the form updating and such involved, not to mention the line-by-line writing to the file. This is pretty much exactly why I was looking at reducing the number of extra calls by doing a full dump in one line of code.  The problem is that if I use the "one-liner", the form is completely unresponsive until the process is complete.
I've tried moving the code for the "all-in-one-shot" dump from the SECOND ATTEMPT into a separate Async method, but I'm extremely unfamiliar with async methods in general, so I'm (obviously) not doing it right:
Private Async Sub OutputToFile(ByVal COPYReader As NpgsqlCopyTextReader, ByVal DestinationFile As IO.FileInfo)
    ' ** METHOD 3
    Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
        Await TestOutputFile.WriteAsync(COPYReader.ReadToEndAsync.Result)
    End Using
    ' ** METHOD 3 RESULTS
    ' ** Records Retrieved: 65358
    ' ** Time To Complete: 0:15.07
    ' ** Lines Written: 34
    ' ** File Size: 4 KB
End Sub
One other thing to mention: I tried moving all of this to a BackgroundWorker, but I was getting some strange behavior when I tried to call my UpdateForm.UpdateProgress method which caused the application to completely skip the actual dumping process.  I've currently given up on trying to get this onto a separate thread, but I'm still open to other suggestions.  This is actually one of the smaller tables that I'm dumping, so I'm not looking forward to what one of the bigger ones will do.
Just for the sake of completeness, here's the UpdateForm class that I've implemented in my library for reusability across other applications:
Imports System.Windows.Forms
Namespace Common
    Public Class FormHandler
        Implements IDisposable
        Public Property ApplicationForm As Form
        Public Property ApplicationStatusLabel As Label
        Public Property ApplicationToolStripLabel As ToolStripStatusLabel
        Public Property ApplicationProgressBar As ProgressBar
        Private LabelVisibleState As Boolean = True
        Private ProgressBarVisibleState As Boolean = True
        Private CurrentStatusText As String
        Private CurrentProgress As Integer
        Public Sub New(ByVal AppForm As Form)
            ApplicationForm = AppForm
        End Sub
        Public Sub New(ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
            ApplicationStatusLabel = StatusLabel
            ApplicationToolStripLabel = Nothing
            ApplicationProgressBar = Progress
            ApplicationForm = ApplicationProgressBar.Parent.FindForm
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationStatusLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Public Sub New(ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
            ApplicationToolStripLabel = StatusLabel
            ApplicationStatusLabel = Nothing
            ApplicationProgressBar = Progress
            ApplicationForm = ApplicationProgressBar.Parent.FindForm
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationToolStripLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
            ApplicationForm = AppForm
            ApplicationStatusLabel = StatusLabel
            ApplicationToolStripLabel = Nothing
            ApplicationProgressBar = Progress
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationStatusLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
            ApplicationForm = AppForm
            ApplicationToolStripLabel = StatusLabel
            ApplicationStatusLabel = Nothing
            ApplicationProgressBar = Progress
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationToolStripLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Friend Sub UpdateProgress(ByVal StatusText As String, ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
            CurrentStatusText = StatusText
            CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal StatusText As String, ByVal PercentComplete As Decimal)
            CurrentStatusText = StatusText
            CurrentProgress = Convert.ToInt32(PercentComplete)
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal StatusText As String)
            CurrentStatusText = StatusText
            CurrentProgress = 0
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal PercentComplete As Decimal)
            CurrentProgress = Convert.ToInt32(PercentComplete)
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
            CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
            UpdateStatus()
        End Sub
        Friend Sub ResetProgressUpdate()
            CurrentStatusText = ""
            CurrentProgress = 0
            UpdateStatus()
        End Sub
        Private Sub UpdateStatus()
            If Not ApplicationForm Is Nothing Then
                If ApplicationForm.InvokeRequired Then
                    Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
                    Try
                        ApplicationForm.Invoke(UpdateInvoker)
                    Catch ex As Exception
                        Dim InvokeError As New ErrorHandler(ex)
                        InvokeError.LogException()
                    End Try
                Else
                    UpdateApplicationProgress(CurrentStatusText)
                End If
            End If
        End Sub
        Friend Sub UpdateApplicationProgress(ByVal ProgressText As String)
            If Not ApplicationForm Is Nothing Then
                With ApplicationForm
                    If Not ProgressText Is Nothing Then
                        If Not ApplicationStatusLabel Is Nothing Then
                            ApplicationStatusLabel.Text = ProgressText
                        End If
                        If Not ApplicationToolStripLabel Is Nothing Then
                            ApplicationToolStripLabel.Text = ProgressText
                        End If
                    End If
                    If Not ApplicationProgressBar Is Nothing Then
                        ApplicationProgressBar.Value = CurrentProgress
                    End If
                End With
                ApplicationForm.Refresh()
                Application.DoEvents()
            End If
        End Sub
        Public Sub Dispose() Implements IDisposable.Dispose
            If Not ApplicationForm Is Nothing Then
                ApplicationForm.Dispose()
            End If
            If Not ApplicationStatusLabel Is Nothing Then
                ApplicationStatusLabel.Visible = LabelVisibleState
                ApplicationStatusLabel.Dispose()
            End If
            If Not ApplicationToolStripLabel Is Nothing Then
                ApplicationToolStripLabel.Visible = LabelVisibleState
                ApplicationToolStripLabel.Dispose()
            End If
            If Not ApplicationProgressBar Is Nothing Then
                ApplicationProgressBar.Visible = ProgressBarVisibleState
                ApplicationProgressBar.Dispose()
            End If
        End Sub
    End Class
End Namespace
EDIT
Per the suggestion in the comments from @the_lotus, I modified my FIRST ATTEMPT slightly to check the value of the current progress (I declared a CurrentProgress variable as an Integer), and it dramatically improved the time taken:
' ** FOURTH ATTEMPT
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
    With COPYReader
        Dim stopWatch As New Stopwatch
        Dim ts As TimeSpan
        Dim elapsedTime As String
        Dim CurrentProgress As Integer = 0
        stopWatch.Start()
        Dim BufferText As String = .ReadLine
        Do While Not BufferText Is Nothing
            CurrentPosition += 1
            OutputFile.WriteLine(BufferText)
            ' ** Checks to see if the value of the ProgressBar will actually
            ' ** be changed by the CurrentPosition before making a call to
            ' ** UpdateProgress.  If the value doesn't change, don't waste
            ' ** the call
            If Convert.ToInt32((CurrentPosition / MaxRecords) * 100) <> CurrentProgress Then
                CurrentProgress = Convert.ToInt32((CurrentPosition / MaxRecords) * 100)
                If Not UpdateForm Is Nothing Then
                    UpdateForm.UpdateProgress(CurrentProgress)
                End If
            End If
            BufferText = .ReadLine
        Loop
        OutputFile.Flush()
        OutputFile.Close()
        stopWatch.Stop()
        ts = stopWatch.Elapsed
        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
    End With
End Using
' ** FOURTH ATTEMPT RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 0:47.45
' ** Lines Written: 65358
' ** File Size: 8,166 KB
Of course, the form is a "little" less responsive than if I were making the call on every record, but I'm thinking it's worth the trade-off.
EDIT #2
So that I could minimize the amount of code I'd have to retype (read: "copy/paste") each time I use the UpdateProgress method, I've moved the test for changed values over there and it seems to be operating with the same performance improvements.  Again, for the sake of completeness, here's the code for the two private methods involved in doing the actual progress/status update:
Private Sub UpdateStatus()
    If Not ApplicationForm Is Nothing Then
        If ApplicationForm.InvokeRequired Then
            Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
            Try
                ApplicationForm.Invoke(UpdateInvoker)
            Catch ex As Exception
                Dim InvokeError As New ErrorHandler(ex)
                InvokeError.LogException()
            End Try
        Else
            UpdateApplicationProgress()
        End If
    End If
End Sub
Private Sub UpdateApplicationProgress()
    Dim Changed As Boolean = False
    If Not ApplicationForm Is Nothing Then
        With ApplicationForm
            If Not CurrentStatusText Is Nothing Then
                If Not ApplicationStatusLabel Is Nothing Then
                    If ApplicationStatusLabel.Text <> CurrentStatusText Then
                        Changed = True
                        ApplicationStatusLabel.Text = CurrentStatusText
                    End If
                End If
                If Not ApplicationToolStripLabel Is Nothing Then
                    If ApplicationToolStripLabel.Text <> CurrentStatusText Then
                        Changed = True
                        ApplicationToolStripLabel.Text = CurrentStatusText
                    End If
                End If
            End If
            If Not ApplicationProgressBar Is Nothing Then
                If ApplicationProgressBar.Value <> CurrentProgress Then
                    Changed = True
                    ApplicationProgressBar.Value = CurrentProgress
                End If
            End If
        End With
        If Changed Then
            ApplicationForm.Refresh()
        End If
        Application.DoEvents()
    End If
End Sub
Doing it this way also came with the added benefit of returning some of the responsiveness to the form that was previously lost. I hope at least some of this code and information is helpful to someone out there.
vb.net npgsql
 
 
 2
 
 
 
 
 
 Your second attempt might not even go to the database since it could be cached. Also, don't call UpdateProgress on each call. It's not necessary when the percentage didn't even move.
 
 – the_lotus
 Nov 21 '18 at 18:11
 
 
 
 
 
 1
 
 
 
 
 
 @the_lotus - HOLY CRAP! I added a small bit of code to check whether or not the current progress percentage has changed (see edit) before calling- UpdateProgress, and it reduced the amount of time dramatically - down to 0:47:45! What a simple but significant thing to overlook! Thanks for that!
 
 – G_Hosa_Phat
 Nov 21 '18 at 18:20
 
 
 
 
 
 
 
 
 
 
 Can someone please clarify why the downvote and vote to close as "opinion-based"? It is my intent to ask "good" questions and I attempt to provide clear examples of my attempts to answer/resolve the issues on my own so as not to add to the clutter of "poor" questions.
 
 – G_Hosa_Phat
 Nov 27 '18 at 16:38
 
 
 
 
 
 1
 
 
 
 
 
 Some people want stackoverflow to be a place for questions that can be answered with a very clear "one solution" code. In this case, there's not clear cut code for the implementation of a solution. I wouldn't worry to much.
 
 – the_lotus
 Nov 27 '18 at 16:46
 
 
 
 
 
 
 
 
 
 
 Thanks for that. I was just asking because I want to know if there's anything I should be doing to improve the quality of my questions in general.
 
 – G_Hosa_Phat
 Nov 27 '18 at 16:54
 
 
 
add a comment |
So, I'm working on a library for my internal applications that interacts with our PostgreSQL database (amongst many other things). One requirement at the moment is that this library can dump data from the database to a file. I have something working, but I've been trying to improve its performance as much as possible. This is what I'm currently looking at:
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
    With COPYReader
        Dim stopWatch As New Stopwatch
        Dim ts As TimeSpan
        Dim elapsedTime As String
        ' ** FIRST ATTEMPT
        stopWatch.Start()
        Dim BufferText As String = .ReadLine
        Do While Not BufferText Is Nothing
            CurrentPosition += 1
            OutputFile.WriteLine(BufferText)
            If Not UpdateForm Is Nothing Then
                UpdateForm.UpdateProgress(Convert.ToInt32((CurrentPosition / MaxRecords) * 100))
            End If
            BufferText = .ReadLine
        Loop
        OutputFile.Flush()
        OutputFile.Close()
        stopWatch.Stop()
        ts = stopWatch.Elapsed
        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
        ' ** FIRST ATTEMPT RESULTS
        ' ** Records Retrieved: 65358
        ' ** Time To Complete: 2:12.07
        ' ** Lines Written: 65358
        ' ** File Size: 8,166 KB
        ' ** SECOND ATTEMPT
        stopWatch.Start()
        Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
            TestOutputFile.Write(.ReadToEndAsync.Result)
        End Using
        stopWatch.Stop()
        ts = stopWatch.Elapsed
        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
        ' ** SECOND ATTEMPT
        ' ** Records Retrieved: 65358
        ' ** Time To Complete: 1:04.01
        ' ** Lines Written: 65358
        ' ** File Size: 8,102 KB
    End With
End Using
I've run multiple tests of each method and come up with pretty much the same results. FIRST ATTEMPT takes about twice as long as SECOND ATTEMPT
Obviously, the UpdateForm.UpdateProgress method used in FIRST ATTEMPT (used to keep the form responsive and show the current progress of the export) is going to cause the process to take longer because of the form updating and such involved, not to mention the line-by-line writing to the file. This is pretty much exactly why I was looking at reducing the number of extra calls by doing a full dump in one line of code.  The problem is that if I use the "one-liner", the form is completely unresponsive until the process is complete.
I've tried moving the code for the "all-in-one-shot" dump from the SECOND ATTEMPT into a separate Async method, but I'm extremely unfamiliar with async methods in general, so I'm (obviously) not doing it right:
Private Async Sub OutputToFile(ByVal COPYReader As NpgsqlCopyTextReader, ByVal DestinationFile As IO.FileInfo)
    ' ** METHOD 3
    Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
        Await TestOutputFile.WriteAsync(COPYReader.ReadToEndAsync.Result)
    End Using
    ' ** METHOD 3 RESULTS
    ' ** Records Retrieved: 65358
    ' ** Time To Complete: 0:15.07
    ' ** Lines Written: 34
    ' ** File Size: 4 KB
End Sub
One other thing to mention: I tried moving all of this to a BackgroundWorker, but I was getting some strange behavior when I tried to call my UpdateForm.UpdateProgress method which caused the application to completely skip the actual dumping process.  I've currently given up on trying to get this onto a separate thread, but I'm still open to other suggestions.  This is actually one of the smaller tables that I'm dumping, so I'm not looking forward to what one of the bigger ones will do.
Just for the sake of completeness, here's the UpdateForm class that I've implemented in my library for reusability across other applications:
Imports System.Windows.Forms
Namespace Common
    Public Class FormHandler
        Implements IDisposable
        Public Property ApplicationForm As Form
        Public Property ApplicationStatusLabel As Label
        Public Property ApplicationToolStripLabel As ToolStripStatusLabel
        Public Property ApplicationProgressBar As ProgressBar
        Private LabelVisibleState As Boolean = True
        Private ProgressBarVisibleState As Boolean = True
        Private CurrentStatusText As String
        Private CurrentProgress As Integer
        Public Sub New(ByVal AppForm As Form)
            ApplicationForm = AppForm
        End Sub
        Public Sub New(ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
            ApplicationStatusLabel = StatusLabel
            ApplicationToolStripLabel = Nothing
            ApplicationProgressBar = Progress
            ApplicationForm = ApplicationProgressBar.Parent.FindForm
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationStatusLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Public Sub New(ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
            ApplicationToolStripLabel = StatusLabel
            ApplicationStatusLabel = Nothing
            ApplicationProgressBar = Progress
            ApplicationForm = ApplicationProgressBar.Parent.FindForm
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationToolStripLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
            ApplicationForm = AppForm
            ApplicationStatusLabel = StatusLabel
            ApplicationToolStripLabel = Nothing
            ApplicationProgressBar = Progress
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationStatusLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
            ApplicationForm = AppForm
            ApplicationToolStripLabel = StatusLabel
            ApplicationStatusLabel = Nothing
            ApplicationProgressBar = Progress
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationToolStripLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Friend Sub UpdateProgress(ByVal StatusText As String, ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
            CurrentStatusText = StatusText
            CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal StatusText As String, ByVal PercentComplete As Decimal)
            CurrentStatusText = StatusText
            CurrentProgress = Convert.ToInt32(PercentComplete)
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal StatusText As String)
            CurrentStatusText = StatusText
            CurrentProgress = 0
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal PercentComplete As Decimal)
            CurrentProgress = Convert.ToInt32(PercentComplete)
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
            CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
            UpdateStatus()
        End Sub
        Friend Sub ResetProgressUpdate()
            CurrentStatusText = ""
            CurrentProgress = 0
            UpdateStatus()
        End Sub
        Private Sub UpdateStatus()
            If Not ApplicationForm Is Nothing Then
                If ApplicationForm.InvokeRequired Then
                    Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
                    Try
                        ApplicationForm.Invoke(UpdateInvoker)
                    Catch ex As Exception
                        Dim InvokeError As New ErrorHandler(ex)
                        InvokeError.LogException()
                    End Try
                Else
                    UpdateApplicationProgress(CurrentStatusText)
                End If
            End If
        End Sub
        Friend Sub UpdateApplicationProgress(ByVal ProgressText As String)
            If Not ApplicationForm Is Nothing Then
                With ApplicationForm
                    If Not ProgressText Is Nothing Then
                        If Not ApplicationStatusLabel Is Nothing Then
                            ApplicationStatusLabel.Text = ProgressText
                        End If
                        If Not ApplicationToolStripLabel Is Nothing Then
                            ApplicationToolStripLabel.Text = ProgressText
                        End If
                    End If
                    If Not ApplicationProgressBar Is Nothing Then
                        ApplicationProgressBar.Value = CurrentProgress
                    End If
                End With
                ApplicationForm.Refresh()
                Application.DoEvents()
            End If
        End Sub
        Public Sub Dispose() Implements IDisposable.Dispose
            If Not ApplicationForm Is Nothing Then
                ApplicationForm.Dispose()
            End If
            If Not ApplicationStatusLabel Is Nothing Then
                ApplicationStatusLabel.Visible = LabelVisibleState
                ApplicationStatusLabel.Dispose()
            End If
            If Not ApplicationToolStripLabel Is Nothing Then
                ApplicationToolStripLabel.Visible = LabelVisibleState
                ApplicationToolStripLabel.Dispose()
            End If
            If Not ApplicationProgressBar Is Nothing Then
                ApplicationProgressBar.Visible = ProgressBarVisibleState
                ApplicationProgressBar.Dispose()
            End If
        End Sub
    End Class
End Namespace
EDIT
Per the suggestion in the comments from @the_lotus, I modified my FIRST ATTEMPT slightly to check the value of the current progress (I declared a CurrentProgress variable as an Integer), and it dramatically improved the time taken:
' ** FOURTH ATTEMPT
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
    With COPYReader
        Dim stopWatch As New Stopwatch
        Dim ts As TimeSpan
        Dim elapsedTime As String
        Dim CurrentProgress As Integer = 0
        stopWatch.Start()
        Dim BufferText As String = .ReadLine
        Do While Not BufferText Is Nothing
            CurrentPosition += 1
            OutputFile.WriteLine(BufferText)
            ' ** Checks to see if the value of the ProgressBar will actually
            ' ** be changed by the CurrentPosition before making a call to
            ' ** UpdateProgress.  If the value doesn't change, don't waste
            ' ** the call
            If Convert.ToInt32((CurrentPosition / MaxRecords) * 100) <> CurrentProgress Then
                CurrentProgress = Convert.ToInt32((CurrentPosition / MaxRecords) * 100)
                If Not UpdateForm Is Nothing Then
                    UpdateForm.UpdateProgress(CurrentProgress)
                End If
            End If
            BufferText = .ReadLine
        Loop
        OutputFile.Flush()
        OutputFile.Close()
        stopWatch.Stop()
        ts = stopWatch.Elapsed
        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
    End With
End Using
' ** FOURTH ATTEMPT RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 0:47.45
' ** Lines Written: 65358
' ** File Size: 8,166 KB
Of course, the form is a "little" less responsive than if I were making the call on every record, but I'm thinking it's worth the trade-off.
EDIT #2
So that I could minimize the amount of code I'd have to retype (read: "copy/paste") each time I use the UpdateProgress method, I've moved the test for changed values over there and it seems to be operating with the same performance improvements.  Again, for the sake of completeness, here's the code for the two private methods involved in doing the actual progress/status update:
Private Sub UpdateStatus()
    If Not ApplicationForm Is Nothing Then
        If ApplicationForm.InvokeRequired Then
            Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
            Try
                ApplicationForm.Invoke(UpdateInvoker)
            Catch ex As Exception
                Dim InvokeError As New ErrorHandler(ex)
                InvokeError.LogException()
            End Try
        Else
            UpdateApplicationProgress()
        End If
    End If
End Sub
Private Sub UpdateApplicationProgress()
    Dim Changed As Boolean = False
    If Not ApplicationForm Is Nothing Then
        With ApplicationForm
            If Not CurrentStatusText Is Nothing Then
                If Not ApplicationStatusLabel Is Nothing Then
                    If ApplicationStatusLabel.Text <> CurrentStatusText Then
                        Changed = True
                        ApplicationStatusLabel.Text = CurrentStatusText
                    End If
                End If
                If Not ApplicationToolStripLabel Is Nothing Then
                    If ApplicationToolStripLabel.Text <> CurrentStatusText Then
                        Changed = True
                        ApplicationToolStripLabel.Text = CurrentStatusText
                    End If
                End If
            End If
            If Not ApplicationProgressBar Is Nothing Then
                If ApplicationProgressBar.Value <> CurrentProgress Then
                    Changed = True
                    ApplicationProgressBar.Value = CurrentProgress
                End If
            End If
        End With
        If Changed Then
            ApplicationForm.Refresh()
        End If
        Application.DoEvents()
    End If
End Sub
Doing it this way also came with the added benefit of returning some of the responsiveness to the form that was previously lost. I hope at least some of this code and information is helpful to someone out there.
vb.net npgsql
So, I'm working on a library for my internal applications that interacts with our PostgreSQL database (amongst many other things). One requirement at the moment is that this library can dump data from the database to a file. I have something working, but I've been trying to improve its performance as much as possible. This is what I'm currently looking at:
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
    With COPYReader
        Dim stopWatch As New Stopwatch
        Dim ts As TimeSpan
        Dim elapsedTime As String
        ' ** FIRST ATTEMPT
        stopWatch.Start()
        Dim BufferText As String = .ReadLine
        Do While Not BufferText Is Nothing
            CurrentPosition += 1
            OutputFile.WriteLine(BufferText)
            If Not UpdateForm Is Nothing Then
                UpdateForm.UpdateProgress(Convert.ToInt32((CurrentPosition / MaxRecords) * 100))
            End If
            BufferText = .ReadLine
        Loop
        OutputFile.Flush()
        OutputFile.Close()
        stopWatch.Stop()
        ts = stopWatch.Elapsed
        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
        ' ** FIRST ATTEMPT RESULTS
        ' ** Records Retrieved: 65358
        ' ** Time To Complete: 2:12.07
        ' ** Lines Written: 65358
        ' ** File Size: 8,166 KB
        ' ** SECOND ATTEMPT
        stopWatch.Start()
        Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
            TestOutputFile.Write(.ReadToEndAsync.Result)
        End Using
        stopWatch.Stop()
        ts = stopWatch.Elapsed
        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
        ' ** SECOND ATTEMPT
        ' ** Records Retrieved: 65358
        ' ** Time To Complete: 1:04.01
        ' ** Lines Written: 65358
        ' ** File Size: 8,102 KB
    End With
End Using
I've run multiple tests of each method and come up with pretty much the same results. FIRST ATTEMPT takes about twice as long as SECOND ATTEMPT
Obviously, the UpdateForm.UpdateProgress method used in FIRST ATTEMPT (used to keep the form responsive and show the current progress of the export) is going to cause the process to take longer because of the form updating and such involved, not to mention the line-by-line writing to the file. This is pretty much exactly why I was looking at reducing the number of extra calls by doing a full dump in one line of code.  The problem is that if I use the "one-liner", the form is completely unresponsive until the process is complete.
I've tried moving the code for the "all-in-one-shot" dump from the SECOND ATTEMPT into a separate Async method, but I'm extremely unfamiliar with async methods in general, so I'm (obviously) not doing it right:
Private Async Sub OutputToFile(ByVal COPYReader As NpgsqlCopyTextReader, ByVal DestinationFile As IO.FileInfo)
    ' ** METHOD 3
    Using TestOutputFile As New IO.StreamWriter(DestinationFile.FullName.Replace(".TXT", "_TEST.TXT"), False)
        Await TestOutputFile.WriteAsync(COPYReader.ReadToEndAsync.Result)
    End Using
    ' ** METHOD 3 RESULTS
    ' ** Records Retrieved: 65358
    ' ** Time To Complete: 0:15.07
    ' ** Lines Written: 34
    ' ** File Size: 4 KB
End Sub
One other thing to mention: I tried moving all of this to a BackgroundWorker, but I was getting some strange behavior when I tried to call my UpdateForm.UpdateProgress method which caused the application to completely skip the actual dumping process.  I've currently given up on trying to get this onto a separate thread, but I'm still open to other suggestions.  This is actually one of the smaller tables that I'm dumping, so I'm not looking forward to what one of the bigger ones will do.
Just for the sake of completeness, here's the UpdateForm class that I've implemented in my library for reusability across other applications:
Imports System.Windows.Forms
Namespace Common
    Public Class FormHandler
        Implements IDisposable
        Public Property ApplicationForm As Form
        Public Property ApplicationStatusLabel As Label
        Public Property ApplicationToolStripLabel As ToolStripStatusLabel
        Public Property ApplicationProgressBar As ProgressBar
        Private LabelVisibleState As Boolean = True
        Private ProgressBarVisibleState As Boolean = True
        Private CurrentStatusText As String
        Private CurrentProgress As Integer
        Public Sub New(ByVal AppForm As Form)
            ApplicationForm = AppForm
        End Sub
        Public Sub New(ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
            ApplicationStatusLabel = StatusLabel
            ApplicationToolStripLabel = Nothing
            ApplicationProgressBar = Progress
            ApplicationForm = ApplicationProgressBar.Parent.FindForm
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationStatusLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Public Sub New(ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
            ApplicationToolStripLabel = StatusLabel
            ApplicationStatusLabel = Nothing
            ApplicationProgressBar = Progress
            ApplicationForm = ApplicationProgressBar.Parent.FindForm
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationToolStripLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As Label, ByVal Progress As ProgressBar)
            ApplicationForm = AppForm
            ApplicationStatusLabel = StatusLabel
            ApplicationToolStripLabel = Nothing
            ApplicationProgressBar = Progress
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationStatusLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Public Sub New(ByVal AppForm As Form, ByVal StatusLabel As ToolStripStatusLabel, ByVal Progress As ProgressBar)
            ApplicationForm = AppForm
            ApplicationToolStripLabel = StatusLabel
            ApplicationStatusLabel = Nothing
            ApplicationProgressBar = Progress
            LabelVisibleState = StatusLabel.Visible
            ProgressBarVisibleState = Progress.Visible
            With ApplicationProgressBar
                .Minimum = 0
                .Maximum = 100
                .Value = 0
                .Visible = True
            End With
            With ApplicationToolStripLabel
                .Visible = True
                .Text = ""
            End With
        End Sub
        Friend Sub UpdateProgress(ByVal StatusText As String, ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
            CurrentStatusText = StatusText
            CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal StatusText As String, ByVal PercentComplete As Decimal)
            CurrentStatusText = StatusText
            CurrentProgress = Convert.ToInt32(PercentComplete)
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal StatusText As String)
            CurrentStatusText = StatusText
            CurrentProgress = 0
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal PercentComplete As Decimal)
            CurrentProgress = Convert.ToInt32(PercentComplete)
            UpdateStatus()
        End Sub
        Friend Sub UpdateProgress(ByVal CurrentPosition As Integer, ByVal MaxValue As Integer)
            CurrentProgress = Convert.ToInt32((CurrentPosition / MaxValue) * 100)
            UpdateStatus()
        End Sub
        Friend Sub ResetProgressUpdate()
            CurrentStatusText = ""
            CurrentProgress = 0
            UpdateStatus()
        End Sub
        Private Sub UpdateStatus()
            If Not ApplicationForm Is Nothing Then
                If ApplicationForm.InvokeRequired Then
                    Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
                    Try
                        ApplicationForm.Invoke(UpdateInvoker)
                    Catch ex As Exception
                        Dim InvokeError As New ErrorHandler(ex)
                        InvokeError.LogException()
                    End Try
                Else
                    UpdateApplicationProgress(CurrentStatusText)
                End If
            End If
        End Sub
        Friend Sub UpdateApplicationProgress(ByVal ProgressText As String)
            If Not ApplicationForm Is Nothing Then
                With ApplicationForm
                    If Not ProgressText Is Nothing Then
                        If Not ApplicationStatusLabel Is Nothing Then
                            ApplicationStatusLabel.Text = ProgressText
                        End If
                        If Not ApplicationToolStripLabel Is Nothing Then
                            ApplicationToolStripLabel.Text = ProgressText
                        End If
                    End If
                    If Not ApplicationProgressBar Is Nothing Then
                        ApplicationProgressBar.Value = CurrentProgress
                    End If
                End With
                ApplicationForm.Refresh()
                Application.DoEvents()
            End If
        End Sub
        Public Sub Dispose() Implements IDisposable.Dispose
            If Not ApplicationForm Is Nothing Then
                ApplicationForm.Dispose()
            End If
            If Not ApplicationStatusLabel Is Nothing Then
                ApplicationStatusLabel.Visible = LabelVisibleState
                ApplicationStatusLabel.Dispose()
            End If
            If Not ApplicationToolStripLabel Is Nothing Then
                ApplicationToolStripLabel.Visible = LabelVisibleState
                ApplicationToolStripLabel.Dispose()
            End If
            If Not ApplicationProgressBar Is Nothing Then
                ApplicationProgressBar.Visible = ProgressBarVisibleState
                ApplicationProgressBar.Dispose()
            End If
        End Sub
    End Class
End Namespace
EDIT
Per the suggestion in the comments from @the_lotus, I modified my FIRST ATTEMPT slightly to check the value of the current progress (I declared a CurrentProgress variable as an Integer), and it dramatically improved the time taken:
' ** FOURTH ATTEMPT
Using COPYReader As NpgsqlCopyTextReader = CType(CIADB.DBConnection.BeginTextExport(COPYSQL), NpgsqlCopyTextReader)
    With COPYReader
        Dim stopWatch As New Stopwatch
        Dim ts As TimeSpan
        Dim elapsedTime As String
        Dim CurrentProgress As Integer = 0
        stopWatch.Start()
        Dim BufferText As String = .ReadLine
        Do While Not BufferText Is Nothing
            CurrentPosition += 1
            OutputFile.WriteLine(BufferText)
            ' ** Checks to see if the value of the ProgressBar will actually
            ' ** be changed by the CurrentPosition before making a call to
            ' ** UpdateProgress.  If the value doesn't change, don't waste
            ' ** the call
            If Convert.ToInt32((CurrentPosition / MaxRecords) * 100) <> CurrentProgress Then
                CurrentProgress = Convert.ToInt32((CurrentPosition / MaxRecords) * 100)
                If Not UpdateForm Is Nothing Then
                    UpdateForm.UpdateProgress(CurrentProgress)
                End If
            End If
            BufferText = .ReadLine
        Loop
        OutputFile.Flush()
        OutputFile.Close()
        stopWatch.Stop()
        ts = stopWatch.Elapsed
        elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds / 10)
    End With
End Using
' ** FOURTH ATTEMPT RESULTS
' ** Records Retrieved: 65358
' ** Time To Complete: 0:47.45
' ** Lines Written: 65358
' ** File Size: 8,166 KB
Of course, the form is a "little" less responsive than if I were making the call on every record, but I'm thinking it's worth the trade-off.
EDIT #2
So that I could minimize the amount of code I'd have to retype (read: "copy/paste") each time I use the UpdateProgress method, I've moved the test for changed values over there and it seems to be operating with the same performance improvements.  Again, for the sake of completeness, here's the code for the two private methods involved in doing the actual progress/status update:
Private Sub UpdateStatus()
    If Not ApplicationForm Is Nothing Then
        If ApplicationForm.InvokeRequired Then
            Dim UpdateInvoker As New MethodInvoker(AddressOf UpdateStatus)
            Try
                ApplicationForm.Invoke(UpdateInvoker)
            Catch ex As Exception
                Dim InvokeError As New ErrorHandler(ex)
                InvokeError.LogException()
            End Try
        Else
            UpdateApplicationProgress()
        End If
    End If
End Sub
Private Sub UpdateApplicationProgress()
    Dim Changed As Boolean = False
    If Not ApplicationForm Is Nothing Then
        With ApplicationForm
            If Not CurrentStatusText Is Nothing Then
                If Not ApplicationStatusLabel Is Nothing Then
                    If ApplicationStatusLabel.Text <> CurrentStatusText Then
                        Changed = True
                        ApplicationStatusLabel.Text = CurrentStatusText
                    End If
                End If
                If Not ApplicationToolStripLabel Is Nothing Then
                    If ApplicationToolStripLabel.Text <> CurrentStatusText Then
                        Changed = True
                        ApplicationToolStripLabel.Text = CurrentStatusText
                    End If
                End If
            End If
            If Not ApplicationProgressBar Is Nothing Then
                If ApplicationProgressBar.Value <> CurrentProgress Then
                    Changed = True
                    ApplicationProgressBar.Value = CurrentProgress
                End If
            End If
        End With
        If Changed Then
            ApplicationForm.Refresh()
        End If
        Application.DoEvents()
    End If
End Sub
Doing it this way also came with the added benefit of returning some of the responsiveness to the form that was previously lost. I hope at least some of this code and information is helpful to someone out there.
vb.net npgsql
vb.net npgsql
edited Nov 21 '18 at 19:46
G_Hosa_Phat
asked Nov 21 '18 at 18:05


G_Hosa_PhatG_Hosa_Phat
280418
280418
 
 
 2
 
 
 
 
 
 Your second attempt might not even go to the database since it could be cached. Also, don't call UpdateProgress on each call. It's not necessary when the percentage didn't even move.
 
 – the_lotus
 Nov 21 '18 at 18:11
 
 
 
 
 
 1
 
 
 
 
 
 @the_lotus - HOLY CRAP! I added a small bit of code to check whether or not the current progress percentage has changed (see edit) before calling- UpdateProgress, and it reduced the amount of time dramatically - down to 0:47:45! What a simple but significant thing to overlook! Thanks for that!
 
 – G_Hosa_Phat
 Nov 21 '18 at 18:20
 
 
 
 
 
 
 
 
 
 
 Can someone please clarify why the downvote and vote to close as "opinion-based"? It is my intent to ask "good" questions and I attempt to provide clear examples of my attempts to answer/resolve the issues on my own so as not to add to the clutter of "poor" questions.
 
 – G_Hosa_Phat
 Nov 27 '18 at 16:38
 
 
 
 
 
 1
 
 
 
 
 
 Some people want stackoverflow to be a place for questions that can be answered with a very clear "one solution" code. In this case, there's not clear cut code for the implementation of a solution. I wouldn't worry to much.
 
 – the_lotus
 Nov 27 '18 at 16:46
 
 
 
 
 
 
 
 
 
 
 Thanks for that. I was just asking because I want to know if there's anything I should be doing to improve the quality of my questions in general.
 
 – G_Hosa_Phat
 Nov 27 '18 at 16:54
 
 
 
add a comment |
 
 
 2
 
 
 
 
 
 Your second attempt might not even go to the database since it could be cached. Also, don't call UpdateProgress on each call. It's not necessary when the percentage didn't even move.
 
 – the_lotus
 Nov 21 '18 at 18:11
 
 
 
 
 
 1
 
 
 
 
 
 @the_lotus - HOLY CRAP! I added a small bit of code to check whether or not the current progress percentage has changed (see edit) before calling- UpdateProgress, and it reduced the amount of time dramatically - down to 0:47:45! What a simple but significant thing to overlook! Thanks for that!
 
 – G_Hosa_Phat
 Nov 21 '18 at 18:20
 
 
 
 
 
 
 
 
 
 
 Can someone please clarify why the downvote and vote to close as "opinion-based"? It is my intent to ask "good" questions and I attempt to provide clear examples of my attempts to answer/resolve the issues on my own so as not to add to the clutter of "poor" questions.
 
 – G_Hosa_Phat
 Nov 27 '18 at 16:38
 
 
 
 
 
 1
 
 
 
 
 
 Some people want stackoverflow to be a place for questions that can be answered with a very clear "one solution" code. In this case, there's not clear cut code for the implementation of a solution. I wouldn't worry to much.
 
 – the_lotus
 Nov 27 '18 at 16:46
 
 
 
 
 
 
 
 
 
 
 Thanks for that. I was just asking because I want to know if there's anything I should be doing to improve the quality of my questions in general.
 
 – G_Hosa_Phat
 Nov 27 '18 at 16:54
 
 
 
2
2
Your second attempt might not even go to the database since it could be cached. Also, don't call UpdateProgress on each call. It's not necessary when the percentage didn't even move.
– the_lotus
Nov 21 '18 at 18:11
Your second attempt might not even go to the database since it could be cached. Also, don't call UpdateProgress on each call. It's not necessary when the percentage didn't even move.
– the_lotus
Nov 21 '18 at 18:11
1
1
@the_lotus - HOLY CRAP! I added a small bit of code to check whether or not the current progress percentage has changed (see edit) before calling
UpdateProgress, and it reduced the amount of time dramatically - down to 0:47:45!  What a simple but significant thing to overlook!  Thanks for that!– G_Hosa_Phat
Nov 21 '18 at 18:20
@the_lotus - HOLY CRAP! I added a small bit of code to check whether or not the current progress percentage has changed (see edit) before calling
UpdateProgress, and it reduced the amount of time dramatically - down to 0:47:45!  What a simple but significant thing to overlook!  Thanks for that!– G_Hosa_Phat
Nov 21 '18 at 18:20
Can someone please clarify why the downvote and vote to close as "opinion-based"? It is my intent to ask "good" questions and I attempt to provide clear examples of my attempts to answer/resolve the issues on my own so as not to add to the clutter of "poor" questions.
– G_Hosa_Phat
Nov 27 '18 at 16:38
Can someone please clarify why the downvote and vote to close as "opinion-based"? It is my intent to ask "good" questions and I attempt to provide clear examples of my attempts to answer/resolve the issues on my own so as not to add to the clutter of "poor" questions.
– G_Hosa_Phat
Nov 27 '18 at 16:38
1
1
Some people want stackoverflow to be a place for questions that can be answered with a very clear "one solution" code. In this case, there's not clear cut code for the implementation of a solution. I wouldn't worry to much.
– the_lotus
Nov 27 '18 at 16:46
Some people want stackoverflow to be a place for questions that can be answered with a very clear "one solution" code. In this case, there's not clear cut code for the implementation of a solution. I wouldn't worry to much.
– the_lotus
Nov 27 '18 at 16:46
Thanks for that. I was just asking because I want to know if there's anything I should be doing to improve the quality of my questions in general.
– G_Hosa_Phat
Nov 27 '18 at 16:54
Thanks for that. I was just asking because I want to know if there's anything I should be doing to improve the quality of my questions in general.
– G_Hosa_Phat
Nov 27 '18 at 16:54
add a comment |
                                1 Answer
                            1
                        
active
oldest
votes
You don't need to call UpdateProgress on each call. It's not necessary when the percentage didn't even move. Try to do a small check and only update the percentage when needed.
It's also possible that the second attempt is faster because it doesn't go to the database. The data could be cached.
 
 
 
 
 
 
 
 SO MUCH THIS! The completion time improved by about 200% down to approximately 45 seconds. While the form is a bit less responsive because it isn't being updated as frequently, it's still responsive enough that I can work with it and the trade-off is well worth the difference in my opinion. I still find it kinda funny that the file sizes are different, but that's a question for another day.
 
 – G_Hosa_Phat
 Nov 21 '18 at 18:43
 
 
 
 
 
 3
 
 
 
 
 
 @G_Hosa_Phat I suggest you still take the time to look at background worker. There's a way in it to send progress back to the form.
 
 – the_lotus
 Nov 21 '18 at 18:53
 
 
 
 
 
 
 
 
 
 
 I've tried throwing the process into a- BackgroundWorker, but when it would make a call to my- UpdateProgressmethod, it would (apparently) throw an exception during the- Invokemethod that I couldn't trap. I tried wrapping that- Invokemethod in a- Try...Catchblock, same with the- UpdateProgressmethod, and even the entire code block, but the- Invokecall just "jumped" out of the executing code block completely and left my application looping indefinitely waiting for the- BackgroundWorkerto "complete".
 
 – G_Hosa_Phat
 Nov 21 '18 at 19:22
 
 
 
 
 
 
 
 2
 
 
 
 
 
 @G_Hosa_Phat with backgroundwork, don't use your UpdateProgress method. Look at the doc they have an example of how to use ReportProgress and your form handle the ProgressChanged event.
 
 – the_lotus
 Nov 21 '18 at 19:26
 
 
 
 
 
 
 
 
 
 
 Yeah, I wish I still had the code to show, but that was several iterations ago and since I gave up on that method, I deleted all of that code. I may go back and take a look at it again later, but this method seems to be working well enough for now. Thanks for the tip and the links. When I have some "free time" to rebuild this later, I'll certainly take a look.
 
 – G_Hosa_Phat
 Nov 21 '18 at 19:35
 
 
 
add a comment |
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53418109%2flong-running-process-with-responsive-form-performance-improvements%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
                                1 Answer
                            1
                        
active
oldest
votes
                                1 Answer
                            1
                        
active
oldest
votes
active
oldest
votes
active
oldest
votes
You don't need to call UpdateProgress on each call. It's not necessary when the percentage didn't even move. Try to do a small check and only update the percentage when needed.
It's also possible that the second attempt is faster because it doesn't go to the database. The data could be cached.
 
 
 
 
 
 
 
 SO MUCH THIS! The completion time improved by about 200% down to approximately 45 seconds. While the form is a bit less responsive because it isn't being updated as frequently, it's still responsive enough that I can work with it and the trade-off is well worth the difference in my opinion. I still find it kinda funny that the file sizes are different, but that's a question for another day.
 
 – G_Hosa_Phat
 Nov 21 '18 at 18:43
 
 
 
 
 
 3
 
 
 
 
 
 @G_Hosa_Phat I suggest you still take the time to look at background worker. There's a way in it to send progress back to the form.
 
 – the_lotus
 Nov 21 '18 at 18:53
 
 
 
 
 
 
 
 
 
 
 I've tried throwing the process into a- BackgroundWorker, but when it would make a call to my- UpdateProgressmethod, it would (apparently) throw an exception during the- Invokemethod that I couldn't trap. I tried wrapping that- Invokemethod in a- Try...Catchblock, same with the- UpdateProgressmethod, and even the entire code block, but the- Invokecall just "jumped" out of the executing code block completely and left my application looping indefinitely waiting for the- BackgroundWorkerto "complete".
 
 – G_Hosa_Phat
 Nov 21 '18 at 19:22
 
 
 
 
 
 
 
 2
 
 
 
 
 
 @G_Hosa_Phat with backgroundwork, don't use your UpdateProgress method. Look at the doc they have an example of how to use ReportProgress and your form handle the ProgressChanged event.
 
 – the_lotus
 Nov 21 '18 at 19:26
 
 
 
 
 
 
 
 
 
 
 Yeah, I wish I still had the code to show, but that was several iterations ago and since I gave up on that method, I deleted all of that code. I may go back and take a look at it again later, but this method seems to be working well enough for now. Thanks for the tip and the links. When I have some "free time" to rebuild this later, I'll certainly take a look.
 
 – G_Hosa_Phat
 Nov 21 '18 at 19:35
 
 
 
add a comment |
You don't need to call UpdateProgress on each call. It's not necessary when the percentage didn't even move. Try to do a small check and only update the percentage when needed.
It's also possible that the second attempt is faster because it doesn't go to the database. The data could be cached.
 
 
 
 
 
 
 
 SO MUCH THIS! The completion time improved by about 200% down to approximately 45 seconds. While the form is a bit less responsive because it isn't being updated as frequently, it's still responsive enough that I can work with it and the trade-off is well worth the difference in my opinion. I still find it kinda funny that the file sizes are different, but that's a question for another day.
 
 – G_Hosa_Phat
 Nov 21 '18 at 18:43
 
 
 
 
 
 3
 
 
 
 
 
 @G_Hosa_Phat I suggest you still take the time to look at background worker. There's a way in it to send progress back to the form.
 
 – the_lotus
 Nov 21 '18 at 18:53
 
 
 
 
 
 
 
 
 
 
 I've tried throwing the process into a- BackgroundWorker, but when it would make a call to my- UpdateProgressmethod, it would (apparently) throw an exception during the- Invokemethod that I couldn't trap. I tried wrapping that- Invokemethod in a- Try...Catchblock, same with the- UpdateProgressmethod, and even the entire code block, but the- Invokecall just "jumped" out of the executing code block completely and left my application looping indefinitely waiting for the- BackgroundWorkerto "complete".
 
 – G_Hosa_Phat
 Nov 21 '18 at 19:22
 
 
 
 
 
 
 
 2
 
 
 
 
 
 @G_Hosa_Phat with backgroundwork, don't use your UpdateProgress method. Look at the doc they have an example of how to use ReportProgress and your form handle the ProgressChanged event.
 
 – the_lotus
 Nov 21 '18 at 19:26
 
 
 
 
 
 
 
 
 
 
 Yeah, I wish I still had the code to show, but that was several iterations ago and since I gave up on that method, I deleted all of that code. I may go back and take a look at it again later, but this method seems to be working well enough for now. Thanks for the tip and the links. When I have some "free time" to rebuild this later, I'll certainly take a look.
 
 – G_Hosa_Phat
 Nov 21 '18 at 19:35
 
 
 
add a comment |
You don't need to call UpdateProgress on each call. It's not necessary when the percentage didn't even move. Try to do a small check and only update the percentage when needed.
It's also possible that the second attempt is faster because it doesn't go to the database. The data could be cached.
You don't need to call UpdateProgress on each call. It's not necessary when the percentage didn't even move. Try to do a small check and only update the percentage when needed.
It's also possible that the second attempt is faster because it doesn't go to the database. The data could be cached.
answered Nov 21 '18 at 18:34
the_lotusthe_lotus
10.2k12346
10.2k12346
 
 
 
 
 
 
 
 SO MUCH THIS! The completion time improved by about 200% down to approximately 45 seconds. While the form is a bit less responsive because it isn't being updated as frequently, it's still responsive enough that I can work with it and the trade-off is well worth the difference in my opinion. I still find it kinda funny that the file sizes are different, but that's a question for another day.
 
 – G_Hosa_Phat
 Nov 21 '18 at 18:43
 
 
 
 
 
 3
 
 
 
 
 
 @G_Hosa_Phat I suggest you still take the time to look at background worker. There's a way in it to send progress back to the form.
 
 – the_lotus
 Nov 21 '18 at 18:53
 
 
 
 
 
 
 
 
 
 
 I've tried throwing the process into a- BackgroundWorker, but when it would make a call to my- UpdateProgressmethod, it would (apparently) throw an exception during the- Invokemethod that I couldn't trap. I tried wrapping that- Invokemethod in a- Try...Catchblock, same with the- UpdateProgressmethod, and even the entire code block, but the- Invokecall just "jumped" out of the executing code block completely and left my application looping indefinitely waiting for the- BackgroundWorkerto "complete".
 
 – G_Hosa_Phat
 Nov 21 '18 at 19:22
 
 
 
 
 
 
 
 2
 
 
 
 
 
 @G_Hosa_Phat with backgroundwork, don't use your UpdateProgress method. Look at the doc they have an example of how to use ReportProgress and your form handle the ProgressChanged event.
 
 – the_lotus
 Nov 21 '18 at 19:26
 
 
 
 
 
 
 
 
 
 
 Yeah, I wish I still had the code to show, but that was several iterations ago and since I gave up on that method, I deleted all of that code. I may go back and take a look at it again later, but this method seems to be working well enough for now. Thanks for the tip and the links. When I have some "free time" to rebuild this later, I'll certainly take a look.
 
 – G_Hosa_Phat
 Nov 21 '18 at 19:35
 
 
 
add a comment |
 
 
 
 
 
 
 
 SO MUCH THIS! The completion time improved by about 200% down to approximately 45 seconds. While the form is a bit less responsive because it isn't being updated as frequently, it's still responsive enough that I can work with it and the trade-off is well worth the difference in my opinion. I still find it kinda funny that the file sizes are different, but that's a question for another day.
 
 – G_Hosa_Phat
 Nov 21 '18 at 18:43
 
 
 
 
 
 3
 
 
 
 
 
 @G_Hosa_Phat I suggest you still take the time to look at background worker. There's a way in it to send progress back to the form.
 
 – the_lotus
 Nov 21 '18 at 18:53
 
 
 
 
 
 
 
 
 
 
 I've tried throwing the process into a- BackgroundWorker, but when it would make a call to my- UpdateProgressmethod, it would (apparently) throw an exception during the- Invokemethod that I couldn't trap. I tried wrapping that- Invokemethod in a- Try...Catchblock, same with the- UpdateProgressmethod, and even the entire code block, but the- Invokecall just "jumped" out of the executing code block completely and left my application looping indefinitely waiting for the- BackgroundWorkerto "complete".
 
 – G_Hosa_Phat
 Nov 21 '18 at 19:22
 
 
 
 
 
 
 
 2
 
 
 
 
 
 @G_Hosa_Phat with backgroundwork, don't use your UpdateProgress method. Look at the doc they have an example of how to use ReportProgress and your form handle the ProgressChanged event.
 
 – the_lotus
 Nov 21 '18 at 19:26
 
 
 
 
 
 
 
 
 
 
 Yeah, I wish I still had the code to show, but that was several iterations ago and since I gave up on that method, I deleted all of that code. I may go back and take a look at it again later, but this method seems to be working well enough for now. Thanks for the tip and the links. When I have some "free time" to rebuild this later, I'll certainly take a look.
 
 – G_Hosa_Phat
 Nov 21 '18 at 19:35
 
 
 
SO MUCH THIS! The completion time improved by about 200% down to approximately 45 seconds. While the form is a bit less responsive because it isn't being updated as frequently, it's still responsive enough that I can work with it and the trade-off is well worth the difference in my opinion. I still find it kinda funny that the file sizes are different, but that's a question for another day.
– G_Hosa_Phat
Nov 21 '18 at 18:43
SO MUCH THIS! The completion time improved by about 200% down to approximately 45 seconds. While the form is a bit less responsive because it isn't being updated as frequently, it's still responsive enough that I can work with it and the trade-off is well worth the difference in my opinion. I still find it kinda funny that the file sizes are different, but that's a question for another day.
– G_Hosa_Phat
Nov 21 '18 at 18:43
3
3
@G_Hosa_Phat I suggest you still take the time to look at background worker. There's a way in it to send progress back to the form.
– the_lotus
Nov 21 '18 at 18:53
@G_Hosa_Phat I suggest you still take the time to look at background worker. There's a way in it to send progress back to the form.
– the_lotus
Nov 21 '18 at 18:53
I've tried throwing the process into a
BackgroundWorker, but when it would make a call to my UpdateProgress method, it would (apparently) throw an exception during the Invoke method that I couldn't trap.  I tried wrapping that Invoke method in a Try...Catch block, same with the UpdateProgress method, and even the entire code block, but the Invoke call just "jumped" out of the executing code block completely and left my application looping indefinitely waiting for the BackgroundWorker to "complete".– G_Hosa_Phat
Nov 21 '18 at 19:22
I've tried throwing the process into a
BackgroundWorker, but when it would make a call to my UpdateProgress method, it would (apparently) throw an exception during the Invoke method that I couldn't trap.  I tried wrapping that Invoke method in a Try...Catch block, same with the UpdateProgress method, and even the entire code block, but the Invoke call just "jumped" out of the executing code block completely and left my application looping indefinitely waiting for the BackgroundWorker to "complete".– G_Hosa_Phat
Nov 21 '18 at 19:22
2
2
@G_Hosa_Phat with backgroundwork, don't use your UpdateProgress method. Look at the doc they have an example of how to use ReportProgress and your form handle the ProgressChanged event.
– the_lotus
Nov 21 '18 at 19:26
@G_Hosa_Phat with backgroundwork, don't use your UpdateProgress method. Look at the doc they have an example of how to use ReportProgress and your form handle the ProgressChanged event.
– the_lotus
Nov 21 '18 at 19:26
Yeah, I wish I still had the code to show, but that was several iterations ago and since I gave up on that method, I deleted all of that code. I may go back and take a look at it again later, but this method seems to be working well enough for now. Thanks for the tip and the links. When I have some "free time" to rebuild this later, I'll certainly take a look.
– G_Hosa_Phat
Nov 21 '18 at 19:35
Yeah, I wish I still had the code to show, but that was several iterations ago and since I gave up on that method, I deleted all of that code. I may go back and take a look at it again later, but this method seems to be working well enough for now. Thanks for the tip and the links. When I have some "free time" to rebuild this later, I'll certainly take a look.
– G_Hosa_Phat
Nov 21 '18 at 19:35
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53418109%2flong-running-process-with-responsive-form-performance-improvements%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown

 
2
Your second attempt might not even go to the database since it could be cached. Also, don't call UpdateProgress on each call. It's not necessary when the percentage didn't even move.
– the_lotus
Nov 21 '18 at 18:11
1
@the_lotus - HOLY CRAP! I added a small bit of code to check whether or not the current progress percentage has changed (see edit) before calling
UpdateProgress, and it reduced the amount of time dramatically - down to 0:47:45! What a simple but significant thing to overlook! Thanks for that!– G_Hosa_Phat
Nov 21 '18 at 18:20
Can someone please clarify why the downvote and vote to close as "opinion-based"? It is my intent to ask "good" questions and I attempt to provide clear examples of my attempts to answer/resolve the issues on my own so as not to add to the clutter of "poor" questions.
– G_Hosa_Phat
Nov 27 '18 at 16:38
1
Some people want stackoverflow to be a place for questions that can be answered with a very clear "one solution" code. In this case, there's not clear cut code for the implementation of a solution. I wouldn't worry to much.
– the_lotus
Nov 27 '18 at 16:46
Thanks for that. I was just asking because I want to know if there's anything I should be doing to improve the quality of my questions in general.
– G_Hosa_Phat
Nov 27 '18 at 16:54