SRP Utilities 1.5 Released

SRP is pleased to announce the release of SRP Utilities 1.5. This release contains two new methods. The first method is SRP_JSON, and it is a fully featured JSON API. The second is SRP_Stopwatch, a handy tool to make it very easy to benchmark your code. Two small routines that will pack a mighty punch in terms of productivity and ease of use.

SRP_JSON

JSON is quite prolific as a means of data transport thanks to its simple schema and smaller footprint when compared to XML. Recent versions of OpenInsight ship with RTI_JSON, a built-in routine for creating or parsing JSON. It is fully featured, but we found it to be too slow for our needs due to its reliance on running Windows scripts. Thus, SRP_JSON was born: a thin wrapper around the open source json-cpp API. You can read the entire documentation here, but here’s a couple samples to show you how it’s done.

We’ll start with building JSON from scratch, then we’ll show how to parse JSON and extract values. In both cases, we’ll use the following sample JSON:

{"employees":[
    {"firstName":"John", "lastName":"Doe"}, 
    {"firstName":"Anna", "lastName":"Smith"},
    {"firstName":"Peter", "lastName":"Jones"}
]}

Here is how you parse JSON. The following example assumes we have the entire JSON contents above in a variable called SampleJSON:

// Parse the SampleJSON string
ParseResult = SRP_JSON(EmployeesHandle, "PARSE", SampleJSON)

// The result is "" if parsing was successful
If ParseResult EQ "" then
    
    // Success! Now get the employee array
    EmployeeArrayHandle = SRP_JSON(EmployeesHandle, "GET", "employees")
    If EmployeeArrayHandle NE 0 then
        
        // Loop through each employee, extracting first and last name
        NumEmployees = SRP_JSON(EmployeeArrayHandle, "GETCOUNT")
        For iEmployee = 1 to NumEmployees
            FirstName = SRP_JSON(EmployeeArrayHandle, "GETVALUE", "[":i:"].firstName")
            LastName = SRP_JSON(EmployeeArrayHandle, "GETVALUE", "[":i:"].lastName")
        Next iEmployee
        
    end
    
end else
    
    // There was an error in parsing, so show it to the user
    Msg(@Window, ParseResult:@FM:@FM:@FM:"!")
    
end

// We're all done with the JSON entity that was created when parsing
SRP_JSON(EmployeesHandle, "RELEASE")

Now, lets create the sample JSON from scratch:

// Create the root object, which we'll call employees
If SRP_JSON(EmployeesHandle, "NEW") then
    
    // Create a new array
    If SRP_JSON(EmployeeArrayHandle, "NEW", "ARRAY") then
        
        // Add the first employee
        If SRP_JSON(SingleEmployeeHandle, "NEW") then
            SRP_JSON(SingleEmployeeHandle, "SETVALUE", "firstName", "John")
            SRP_JSON(SingleEmployeeHandle, "SETVALUE", "lastName", "Doe")
            SRP_JSON(EmployeeArrayHandle, "ADD", SingleEmployeeHandle)
            SRP_JSON(SingleEmployeeHandle, "RELEASE")
        end
        
        // Add the second employee
        If SRP_JSON(SingleEmployeeHandle, "NEW") then
            SRP_JSON(SingleEmployeeHandle, "SETVALUE", "firstName", "Anna")
            SRP_JSON(SingleEmployeeHandle, "SETVALUE", "lastName", "Smith")
            SRP_JSON(EmployeeArrayHandle, "ADD", SingleEmployeeHandle)
            SRP_JSON(SingleEmployeeHandle, "RELEASE")
        end
        
        // Add the third employee
        If SRP_JSON(SingleEmployeeHandle, "NEW") then
            SRP_JSON(SingleEmployeeHandle, "SETVALUE", "firstName", "Peter")
            SRP_JSON(SingleEmployeeHandle, "SETVALUE", "lastName", "Jones")
            SRP_JSON(EmployeeArrayHandle, "ADD", SingleEmployeeHandle)
            SRP_JSON(SingleEmployeeHandle, "RELEASE")
        end
        
        // Now add the array as a member of the root object
        SRP_JSON(EmployeesHandle, "SET", "employees", EmployeeArrayHandle)
        
        // All done with the array object
        SRP_JSON(EmployeeArrayHandle, "RELEASE")
    
    end
    
    // Now get the actual JSON
    SampleJSON = SRP_JSON(EmployeesHandle, "STRINGIFY", "STYLED")
    
    // All done with the root object
    SRP_JSON(EmployeesHandle, "RELEASE")
    
end

SRP_Stopwatch

Since we create a lot of APIs and tools, often with the goal of squeezing as much performance out of them as possible, we frequently find ourselves writing code to time how long it takes segments of code to execute. To make this process easier, we created SRP_Stopwatch. SRP_Stopwatch lets you time segments of code, giving each segment a name, and then displaying all the results at the end of a long run. You can read the documentation for this method here, but here’s a sample of it in action:

Compile function Test_List_Speed(VOID)

    Declare function SRP_FastArray_Create, SRP_FastArray_GetVariable
    Declare subroutine SRP_Stopwatch, SRP_FastArray_Insert
    
    Iterations = 1000
    InsertText = "Hello, World!"

    // Always call reset at the beginning to clear previous benchmarking runs
    SRP_Stopwatch("Reset")

    // Insert the text at random field, value, and subvalue positions
    SRP_Stopwatch("Start", "OIInsert")
    TestOI = ""
    For i = 1 to Iterations
        TestOI = Insert(TestOI, Rnd(MaxPos), Rnd(MaxPos), Rnd(MaxPos), InsertText)
    Next i
    SRP_Stopwatch("Stop", "OIInsert")

    // Insert the text at random field, value, and subvalue positions using SRP Fast Array
    SRP_Stopwatch("Start", "SRPInsert")
    Handle = SRP_FastArray_Create()
    For i = 1 to Iterations
        SRP_FastArray_Insert(Handle, Rnd(MaxPos), Rnd(MaxPos), Rnd(MaxPos), InsertText)
    Next i
    TestSRP = SRP_FastArray_GetVariable(Handle)
    SRP_Stopwatch("Stop", "SRPInsert")

    // Show both results so we can compare
    SRP_Stopwatch("ShowAll")

Return 1

SRP Utilities 1.5 is available to download immediately, for free!, from our downloads page. We hope you enjoy these new features.

7 Responses to SRP Utilities 1.5 Released

  • Matt Crozier says:

    Speaking of benchmarks, has anyone else found that RTI_StringBuilder() is actually *slower* than conventional string concatenation in OI !?

    Eg
    Call RTI_StringBuilder( h, ‘New)
    a = ”
    for i = 1 to 999999
    item = fmt( i, ‘R(0)#6’)

    * using RTI_StringBuilder: 00h 00m 03s 781ms
    Call RTI_StringBuilder( h, ‘Append’, item: @fm)

    * using string concatenation: 00h 00m 02s 297ms
    * a := item: @fm

    next i
    Call RTI_StringBuilder( h, ‘Destroy’)

    • Don Bakke says:

      Matt,

      Simple BASIC+ string handling will often be faster than wrappers like RTI_StringBuilder or even some of our utilities. As noted in our own documentation, “As with everything, use the right tool for the right job.” I also covered this in my December 8, 2014 response on the WORKS forum when someone asked what the fastest way was to select all records and build an internal array.

      • Matt Crozier says:

        Hi Don – yeah, I saw that post. I’m surprised at this result because I thought the whole point of RTI_StringBuilder() was to speed up this kind of processing. I’m not quite sure where it would be useful otherwise.

        • Don Bakke says:

          Matt,

          If all you need to do is build a simple string then it probably won’t get any quicker than simple BASIC+. As you probably know, you can get even better performance by pre-sizing the string with characters and then fill the string using [] operators. The logic is more complex but the speed improvements are incredible.

          If the string gets significantly larger (like increasing your iteration loop by a factor of 10) then you will see speed benefits with RTI_StringBuilder. So there is a sweet spot where one method becomes a better option. Most operations we tend to perform are small enough where simple BASIC+ is better.

          • Matt Crozier says:

            Ok – for a 67Mb string, RTI_StringBuilder() takes around 04m 37s; := concatenation takes about 04m 29s. It seems these strings have to be *very* large before you see some gains.

            BTW – yes, using putBinaryValue() into a pre-sized string is the fastest method I’ve found, regardless of string size.

  • Don Bakke says:

    It’s funny how the benchmark results can vary in-between tests, even when nothing has changed in the test logic. When I first posted my results for a 67MB string, RTI_StringBuilder was almost 3 minutes faster. In my second test it is now 30 seconds slower. I also noticed that overall your speeds are always faster than my own, which is surprising as I have pretty fast gear.

    As I had mentioned in the WORKS post, if you need random access to your string data then I suspect you’ll find RTI_StringBuilder to have proven itself quite well. Similarly, the SRP_JSON utility will never be as fast as simple string building. However, string building JSON requires careful tracking of the various identifiers of {} for objects, [] for arrays, and commas for key/value separators. SRP_JSON makes it so much easier to both create JSON and work within the existing string for random access extraction or updating.

Leave a Reply to Don BakkeCancel reply