LotusScript taking 30-35 seconds to process

Is there any reason that this code should take 30-35 seconds to execute on the client? I’m pretty new at LotusScript and suspect that I have some poor coding in here. This button is on a doc that copies (and negates) specific fields to a new doc, saves the new doc, closes the UI doc, refreshes the current view, and then refreshes a view used for a DB lookup.

Sub Click(Source As Button)

'Action to copy current document reversing the hours requested entries,

'submitting to supervisor, and effectively cancelling request

Dim session As New NotesSession	

Dim db As NotesDatabase

Dim workspace As New NotesUIWorkspace

Dim view As NotesView	

Dim uidoc As NotesUIDocument

Dim doc As NotesDocument

Dim doc2 As NotesDocument

Dim success As Variant

Set db = session.CurrentDatabase

Set view = db.GetView("CheckDupCancel")

'Get document, copy, change fields, then leave in Edit mode so staff can review and submit or CANCEL

Set uidoc = workspace.CurrentDocument

Set doc = uidoc.Document

Set doc2 = New NotesDocument (db)

Call doc.CopyAllItems (doc2, True )

'Set fields to new values

Dim sick,ppd,ipp,vac,hol,per As Variant

Dim negate As Integer

Dim Rev As String

negate = (-1)

sick=negate * doc2.SickDays(0)

ppd = negate * doc2.PPDdays(0)

ipp = negate * doc2.IPPdays(0)

vac= negate * doc2.VacDays(0)

hol = negate * doc2.HolidayDays(0)

per = negate * doc2.PersonalDays(0)

oth= negate * doc2.OtherDays(0)

Rev=doc2.ReviewerLog(0)

Set notesItem = doc2.ReplaceItemValue("SickDays", sick)

Set notesItem = doc2.ReplaceItemValue("PPDdays", ppd)

Set notesItem = doc2.ReplaceItemValue("IPPdays", ipp)

Set notesItem = doc2.ReplaceItemValue("VacDays", vac)

Set notesItem = doc2.ReplaceItemValue("HolidayDays", hol)

Set notesItem = doc2.ReplaceItemValue("PersonalDays", per)

Set notesItem = doc2.ReplaceItemValue("OtherDays", per)

Set notesItem = doc2.ReplaceItemValue("Form", "Cancel")

Set notesItem = doc2.ReplaceItemValue("Approved", "")

Set notesItem = doc2.ReplaceItemValue("Comment", "")

Set notesItem = doc2.ReplaceItemValue("dspApproved", "")

Set notesItem = doc2.ReplaceItemValue("dspComments", "")

Set notesItem = doc2.ReplaceItemValue("ReviewerLog", Rev)

Set notesItem = doc2.ReplaceItemValue("Status", 1)

Set notesItem = doc2.ReplaceItemValue("CurrentEditor",doc2.ReviewerList)

doc2.RemoveItem("SaveOptions")

doc2.RemoveItem("CurrentUser")

doc2.RemoveItem("SubmitNow")

doc2.RemoveItem("NSubmitNow")

doc2.RemoveItem("Resubmit")

doc2.RemoveItem("SendError")

doc2.RemoveItem("WebCategories")

success = doc2.ComputeWithForm( False, False )

If success Then

	Call doc2.Save( True, True )

End If

’ Call doc2.Save (False, False) 'Save new doc

Call uidoc.Close(True)

Call workspace.ViewRefresh 	

Messagebox  ("Your supervisor has been notified that this Cancellation requires review. If approved by your supervisor, this request will be cancelled. It will still appear in your records, but the hours will be added back to your total.")

Call view.Refresh	

End Sub

Subject: RE: LotusScript taking 30-35 seconds to process

Have you stepped thru the code to determine where the speed bottleneck is? I suspect that you will find the majority of the time will be spent refreshing those views (depending on how many documents there are in the database/view). Also, does the form design have alot of calculated fields/formulas? The “ComputeWithForm” can take some time on very large forms as well (I believe).

Hope that helps!

T.

Subject: RE: LotusScript taking 30-35 seconds to process

Pam, in addition or in conjunction with what Terry said, what you might do is put some Print statements in the code, perhaps before the Compute With Form and after it.

I would use something like

Print "Started CWF " & Time$

Print "Finished CWF " & Time$

Maybe other places also.

When the code finishes, click in the Status area of the client to see all the print output and that would give you an idea of the elapsed time for each area of your code.

Subject: RE: LotusScript taking 30-35 seconds to process

In addition to the other suggestions, there are a few things scriptwise that will increase the speed.

For instance, calling Doc2 a few dozen times will slow down processing, i.e.:

set item = doc2…

So, instead, you should think about using with doc2…end with, i.e.:

With doc2

	sick=negate * .SickDays(0)

	ppd = negate * .PPDdays(0)

	ipp = negate * .IPPdays(0)

	vac= negate * .VacDays(0)

	hol = negate * .HolidayDays(0)

	per = negate * .PersonalDays(0)

	oth= negate * .OtherDays(0)

	Rev=.ReviewerLog(0)

	.ReplaceItemValue "SickDays", sick

	.ReplaceItemValue "PPDdays", ppd

	.ReplaceItemValue "IPPdays", ipp

	.ReplaceItemValue "VacDays", vac

	.ReplaceItemValue "HolidayDays", hol

	.ReplaceItemValue "PersonalDays", per

	.ReplaceItemValue "OtherDays", per

	.ReplaceItemValue "Form", "Cancel"

	.ReplaceItemValue "Approved", ""

	.ReplaceItemValue "Comment", ""

	.ReplaceItemValue "dspApproved", ""

	.ReplaceItemValue "dspComments", ""

	.ReplaceItemValue "ReviewerLog", Rev

	.ReplaceItemValue "Status", 1

	.ReplaceItemValue "CurrentEditor",.ReviewerList

	.RemoveItem "SaveOptions"

	.RemoveItem"CurrentUser"

	.RemoveItem "SubmitNow"

	.RemoveItem "NSubmitNow"

	.RemoveItem "Resubmit"

	.RemoveItem "SendError"

	.RemoveItem "WebCategories"

	success = .ComputeWithForm(False, False)

	If success Then

		.Save  True, False, True

	End If

     end with

Computewithform sometimes has a severe processing affect, but you’re not using it to loop through a number of documents. So, this might be a place to put a print statement to see how long it takes to get to this point, and how long it takes to process after that: print “time:” + format(now)

Other than that it could simply be your wan or LAN that is slowing down. But see if wrapping doc2 helps a bit.

Subject: RE: LotusScript taking 30-35 seconds to process

I’d be very surprised to find that the “with” statement makes any speed difference at all. Do you have timing data that indicates this?

Subject: RE: LotusScript taking 30-35 seconds to process

from my VB days…:slight_smile:

[q]

With…End With allows you to perform a series of statements on a specified object without requalifying the name of the object. If the qualification path to the object is long, using With…End With can improve your performance. A With block also reduces repetitive typing of the qualification path and the risk of mistyping one of its elements.

[q]

If you make many references to members of an element that is qualified, such as MyForm.Controls.Item(Subscript), you can improve performance by using the With … End With construction:

With MyForm.Controls.Item(Subscript) ’ Evaluate the qualification once.

.Name = "Control number " & CStr(Subscript)

.Text = .Name

’ Access other members of MyForm.Controls.Item(Subscript)

.Refresh()

End With

The preceding code evaluates MyForm.Controls.Item(Subscript) only once. It can run more than twice as fast as requalifying every member access. However, if the element is not qualified, for example AnswerForm or Me, there is no performance improvement using With … End With. For more information, see With…End With Statements.


across a WAN, I’ve seen this simple technique reduce a slow, complicated agent significantly.

If nothing else, makes typing easier.

Subject: I ran some tests, and…

while the WITH statement does save some typing, it doesn’t seem to make a measurable difference in performance. It might if you are dereferencing long chains of objects (as in a.b.c.d.e.f.document.field), but not in the normal sort of code we usually see where the document object is already fully dereferenced.

I ran 100 trials of 1000 iterations, with ten assignments in each iteration, for a total of 1,000,000 operations of each of four types. Test 1 used the WITH statement to reference a pre-assigned document object. Test 2 used the WITH statement to reference NotesUIDocument.Document. Test 3 used NotesUIDocument.Document in each assignment. Test 4 used a pre-assigned document object in each assignment.

The results:

Test 1: 0.0006563 seconds per operation

Test 2: 0.0006561 seconds per operation

Test 3: 0.0006601 seconds per operation

Test 4: 0.0006566 seconds per operation

Test 3, which dereferences the NotesUIDocument.Document object in every operation was the slowest, but by only 4 millionths of a second per operation. The other three tests varied from each other by less than 4 ten-millionths of a second per operation – which I believe is good evidence that they all effectively compile into a equivalent code – i.e., that the WITH statements effectively create a temporary object variable that is accessed in the same way as one that is explicitly set (via set doc=uidoc.Document statement).

Test code below.

Sub Click(Source As Button)

Const max  = 1000&

Const trials = 100&



Dim ws As New NotesUIWorkspace

Dim uidoc As NotesUIDocument

Dim doc As NotesDocument

Dim t1 As Double

Dim t2 As Double

Dim t3 As Double

Dim t4 As Double

Dim tps As Long

Dim stc As Long

Dim ftc As Long

Dim ops As Double



ops = max * trials * 10



Set uidoc = ws.CurrentDocument

Set doc = uidoc.document	

tps = Getthreadinfo(7) 'get the ticks per second



' Test 1



stc = Getthreadinfo(6) 'get the starting tick count



With doc

	For i = 1 To trials

		For j = 1 To max			

			.field_1 = "a" + Cstr(i)

			.field_2 = "b" + Cstr(i)

			.field_3 = "c" + Cstr(i)

			.field_4 = "d" + Cstr(i)

			.field_5 = "e" + Cstr(i)

			.field_6 = "f" + Cstr(i)

			.field_7 = "g" + Cstr(i)

			.field_8 = "h" + Cstr(i)

			.field_9 = "i" + Cstr(i)

			.field_10 = "j" + Cstr(i)

		Next		

		Print "1 " + Cstr(i)		

	Next

End With



ftc = Getthreadinfo(6) 'get the final tick count

t1 = (ftc - stc) / tps  'final tick count minus starting tick count divided by ticks per second yields time.



' Test 2



stc = Getthreadinfo(6) 'get the starting tick count



With uidoc.document	

	For i = 1 To trials

		For j = 1 To max			

			.field_1 = "a" + Cstr(i)

			.field_2 = "b" + Cstr(i)

			.field_3 = "c" + Cstr(i)

			.field_4 = "d" + Cstr(i)

			.field_5 = "e" + Cstr(i)

			.field_6 = "f" + Cstr(i)

			.field_7 = "g" + Cstr(i)

			.field_8 = "h" + Cstr(i)

			.field_9 = "i" + Cstr(i)

			.field_10 = "j" + Cstr(i)

		Next		

		Print "2 " + Cstr(i)		

	Next	

End With



ftc = Getthreadinfo(6) 'get the final tick count	

t2 = (ftc - stc) / tps  'final tick count minus starting tick count divided by ticks per second yields time.



'Test 3



stc = Getthreadinfo(6) 'get the starting tick count



For i = 1 To trials

	For j = 1 To max		

		uidoc.document.field_1 = "a" + Cstr(i)

		uidoc.document.field_2 = "b" + Cstr(i)

		uidoc.document.field_3 = "c" + Cstr(i)

		uidoc.document.field_4 = "d" + Cstr(i)

		uidoc.document.field_5 = "e" + Cstr(i)

		uidoc.document.field_6 = "f" + Cstr(i)

		uidoc.document.field_7 = "g" + Cstr(i)

		uidoc.document.field_8 = "h" + Cstr(i)

		uidoc.document.field_9 = "i" + Cstr(i)

		uidoc.document.field_10 = "j" + Cstr(i)

	Next		

	Print "3 " + Cstr(i)			

Next



ftc = Getthreadinfo(6) 'get the final tick count	

t3 = (ftc - stc) / tps  'final tick count minus starting tick count divided by ticks per second yields time.



' Test 4



stc = Getthreadinfo(6) 'get the starting tick count



For i = 1 To trials

	For j = 1 To max		

		doc.field_1 = "a" + Cstr(i)

		doc.field_2 = "b" + Cstr(i)

		doc.field_3 = "c" + Cstr(i)

		doc.field_4 = "d" + Cstr(i)

		doc.field_5 = "e" + Cstr(i)

		doc.field_6 = "f" + Cstr(i)

		doc.field_7 = "g" + Cstr(i)

		doc.field_8 = "h" + Cstr(i)

		doc.field_9 = "i" + Cstr(i)

		doc.field_10 = "j" + Cstr(i)

	Next		

	Print "4 " + Cstr(i)			

Next



ftc = Getthreadinfo(6) 'get the final tick count



t4 = (ftc - stc) / tps  'final tick count minus starting tick count divided by ticks per second yields time.



' Results



Call ws.Prompt(PROMPT_OK,"Times",  "With doc = " + Cstr(t1/ops) + " with uidoc.document" + Cstr(t2/ops) + " uidoc.Document " + Cstr(t3/ops) + " doc " + Cstr(t4/ops) )

End Sub

Subject: RE: I ran some tests, and…

Thanks Rick,

I stand corrected on this point. I haven’t really tested for awhile now… you know, old habits die hard :slight_smile:

Thanks for running the tests… and Happy New Year!

Subject: Thanks to all for your input!

Yes, it’s probably the last refresh! I’m refreshing because I have a DB lookup that will then prevent submitting a duplicate document. Maybe I can figure out a better way of doing that! Thanks again!

Subject: LotusScript taking 30-35 seconds to process

You are calling Save twice on doc2.

Get rid of that computewith form if at all possible. That’s an extremely expensive operation.

And don’t use Variants where you know the datatype of what you are working with:

Dim sick As Variant Double

sick=negate * doc2.SickDays(0)

Numeric items return an array of doubles. You don’t need a datatype that big for this purpose, so sick should be an Integer, and you should cast the Double that you get back from SickDays into integer:

Dim sick As integer

sick=negate * cint(doc2.SickDays(0))

Of course negate is already an integer so Notes is casting the double into integer anyway. You should do so explicitly anyway, becuase not doing so will sooner or later cause some very hard to find bugs.

And if you don’t need the handle to the newly created notesitem you should just use call here:

call doc2.ReplaceItemValue(“SickDays”, sick)

You can declare and instantiate things like notesuiworkspace in one step but I think this makes your script harder to read. Declare first, then instantiate. Front end object declarations first, then back end objects in decreasing order of size:

Dim workspace As NotesUIWorkspace

Dim uidoc As NotesUIDocument

Dim success As Variant

Dim session As NotesSession

Dim db As NotesDatabase

Dim view As NotesView

Dim doc As NotesDocument

Dim doc2 As NotesDocument

set workspace = new notesuiworkspace

set uidoc = workspace.currentdocument

Set db = session.CurrentDatabase

Set view = db.GetView(“CheckDupCancel”)

Don’t do things like this:

Set notesItem = doc2.ReplaceItemValue(“CurrentEditor”,doc2.ReviewerList)

If the document happens not to have a ReviewerList item nothing will happen, and your results will be off and you won’t know why. Instead:

call doc2.ReplaceItemValue(“CurrentEditor”,doc2.getfirstitem(“ReviewerList”).values)

That way if doc2 has no such item you will get an exception that tells you something is wrong. Or you can use .hasitem().