Optimize?

I have profiled my code and found that too much time is spent in this method:

[code]Function Operator_Convert() As String
dim x as string=""
dim len as integer

len = mb.Size()
if len > 0 then
if mb.Byte(len-1) = 0 then
len = len - 1
end if
x = mb.StringValue(0, len)
end if
return x

End Function
[/code]
Any suggestions for an optimization to this method or class?

It seems like you’re extracting C-Strings?

While not tested, you could try.

if mb.size > 0 then return mb.cString( 0 )

It shouldn’t, but I wonder if your use of the reserved word “len” is a factor.

A DCM_UI is a list of digits that must be an even number of digits long (In the memory block.)
If the length is odd an extra chr(0) is added…

but having an extra nul character in a string messes up string comparisons where there is no nul character but everything else is the same.

There’s not much to optimize there. If you’re spending too much time in that method, then you’re calling it a lot. That’s where you should optimize - in the calling routines.

The only thing you could optimize about it is not set x explicitly to “” in the Dim statement. Just leave it

dim x as string

The framework initialized it to “” anyway. You don’t need to initilize it again. That would reduce it a little.

If you still wanted to keep using stringValue, then you could try this.

[code] Dim len as integer = mb.size

if len > 0 then
if mb.byte( len-1 ) = 0 then
return mb.stringvalue( 0, len-1 )
else
return mb.stringvalue( 0, len )
end if
end if[/code]

Like Tim point’s out, sometimes you can actually save time by doing the calculation more than once, rather than storing the value in a property, but we’re talking about shaving off 1~10 milliseconds per transaction.

I still think your best bet is to use cString as it will auto strip off the trailing null byte ( 0 ).

I guess if converting from the DCM_UI type to string is having to be done too often then perhaps the memory block needs to ‘go’ and be replaced by a string.

Thanks everyone for the help.
CString seemed like a good idea, so i tried it and got this stranger error:
How can there be an out of bounds exception if the debugger can see the data?

Theory: Because this byte sequence does not contain a null, it goes out of bounds expecting to find one. Can you initialize your MemoryBlock to one larger than you need to ensure the null at the end?

Or go back to your original method of checking.

just a thought (untested)

  try
    return mb.CString(0)
  catch OutOfBoundsException
    Return mb
  end try  

EDIT: after some thought this could only be faster IF mb.size > 0. Otherwise the exception handeling will slow it down.

EDIT2: However, this is faster and solves the ubound error:

in a module, declare:

mb0 As memoryBlock
Sub Init() ' run this once at the startup of your app to initialize it
  mb0 = NewMemoryBlock(0)
End Sub

In the class:

Function Operator_Convert() As String
  if mb <> mb0 then
    return mb.CString(0)
  end if  
End Function

Ah… I then must apologize as my suggestion obviously doesn’t work, sorry about that.

That’s a good tip Alain. I would have expected that to fail because they were two different MemoryBlock instances, but it looks like the actual bytes are compared.

Also, I reproduced the OutOfBounds exception if there is no trailing null, so I suggest something like this:

if mb <> mb0 then
  dim p as Ptr = mb
  dim lastByteIndex as integer = mb.Size - 1
  if p.Byte( lastByteIndex ) = 0 then
    return mb.CString( 0 )
  else
    return mb
  end if
end if
return ""

Either that or, when setting mb initially, always create it as one byte more than needed so there is always a trailing null. In that case, all you ever need is:

return mb.CString( 0 )

Kem, why do you still need the lastByteIndex test? With my code

Function Operator_Convert() As String
  if mb <> mb0 then
    return mb.CString(0)
  end if  
End Function

I don’t get an OutOfBoundsException. Or am I missing something?

Test code:

  dim s1, s2, s3 as string
  dim a as Integer
  
  dim c1 as new Class1(0)
  s1 = c1 ' result s1 = nothing
  
  dim c2 as new Class1(7)
  for a = 0 to 6
    c2.mb.Byte(a) = a+49
  next
  s2 = c2 ' result s2 = 1234567, c2 = binary 31 32 33 34 35 36 37
  
  dim c3 as new Class1(8)
  for a = 0 to 6
    c3.mb.Byte(a) = a+49
  next
  c3.mb.Byte(7) = 0
  s3 = c3 ' result also s3 = 1234567, c3 = binary 31 32 33 34 35 36 37 00

It’s probably a platform-specific error.

Tim, I tested it on Windows 7 (RB2007R4) and OSX (Xojo 2013 R2). Both worked as expected without the exception.

They this very simple test:

  dim mb as MemoryBlock = "this string"
  dim s as string = mb.CString( 0 )
  MsgBox s

I can run this, at most, once. If I try to run it again, I get an OutOfBounds error. Since it works sometimes, it’s some kind of bug, but I don’t know what.

This is on MacOS 10.9, btw.

Is indeed a OSX only problem. Not a problem in Windows. Xojo on OSX vs. Xojo on Windows: 1000 - 1 :slight_smile:

I’ll file a Feedback report.

UPDATE:

<https://xojo.com/issue/30963>

What have I done now?! :slight_smile:

FYI, the bug has been marked fixed for an upcoming release. I’m not sure if the “fix” is to return the contents of the MemoryBlock when there is no null terminator, or to generate the Exception consistently.