BLOGS

COMMENT

warning: file_get_contents(http://www.telize.com/geoip/54.80.10.56) [function.file-get-contents]: failed to open stream: HTTP request failed! HTTP/1.1 404 Not Found in /var/www/www.4d.com/docs/includes/common.inc(1762) : eval()'d code on line 4.

Doing It the 4D v13 Way: Making It Simple and Fast

And here we are! 4D v13 was released on February 14. The virtual boxes pictures are pretty cool, aren't they?

This version comes with incredible(1) new functionalities that you can discover - or have already discovered - on the web site and/or in the upgrade documentation, or during our last webinar about 4D v13.

 

Today, I would like to spend some time on one of the three main points of this version: pro-duc-ti-vi-ty:

 

 

More precisely, I'd like to insist on a sub-point in this point. Increased productivity can be a huge topic, so I am going to highlight two of them:

  1. How to write more simple code with 4D v13, and
  2. how to - at the same time - make this code run faster. Sometimes way faster, as we will see in a few seconds.

We will increase our productivity by reducing the number of lines of code and simplifying maintenance and debugging. In short, we will save development time, which means - ultimately - we will be able to accomplish more in the same amount of time. And that perfectly fits with the notion of increased productivity. Note that instead of using this saved time to code even more - whcih means working more - we might instead increase our time to actually relax. The thing is that we love coding.

 

OK.

 

Back on topic: we are also going to increase end-users' productivity, since the new code will run much faster. They'll can do more in the same time. Or spend more time in relaxing, doing social-networking, reading interesting document or whatever. Finally, well, they do what they want with this saved time. We write code and that's it.

 

To illustrate my point, I'll be using the Sum command. You surely know this command which is as old as 4D itself(2). Prior to version 13, Sum (and more generally speaking, all the commands of the "On a series" theme - and what a strange name, don't you think?) used to work only with a selection of records(3).

 

QUERY([Invoices];[invoices]Paid = False)

$unpaid:=Sum([Invoices]Amount)

 

As of 4D v13, this function can receive a numeric array (real, long, integer) as a parameter. Which means that your old code, that adds every element of an array in a loop…
 

ARRAY REAL($arrValues;0)

. . . fill the array . . .

$total:=0

For($i;1;Size of array($arrValues))

    $total:=$total + $arrValues{$i}

End for

…can be replaced with a single line of code:
 

ARRAY REAL($arrValues;0)

. . . fill the array . . .

$total:=Sum($arrValues)

If you are a well organized developer, you probably already have written a generic method for this purpose. Something like this (I'm saving you from the error-checking code to focus on the heart of the method):
 

// Method $result:=DoSumArray(->anArray)

C_POINTER($1)

C_REAL($0)

C_LONGINT($i)

 

$0:=0

For($i;1;ize of array($1->))

    $0:=$0+$1->{$i}

End for

You called this method everywhere you needed to…
 

ARRAY REAL($arrValues;0)

. . . fill the array . . .

$total:=DoSumArray(->$arrValues)

…and you have been smart, because you're going to update your application in less than five seconds:


    2 seconds to open the DoSumArray method
    3 seconds to replace the code that loops in the array with a call to Sum:
 

// Method $result:=DoSumArray(->anArray)

C_POINTER($1)

C_REAL($0)

 

$0:=Sum($1->)

Later, when you'll be doing your bi-annual-code-cleanup, you'll take only the time necessary to delete the DoSumArray method, to replace it everywhere with a call to Sum. Just think about removing the pointer sign in the call ("->"). 

 

Using Sum on your arrays now, you have not only improved the readability of the code (one line, with a clear label) and the maintenance time(4). You also have a huge increase in performance, because the 4D command runs in C++ (well, more precisely in compiled C++), very close to the CPU, and because it's precisely what CPUs are good at: Calculating.

 

Here are the results of some benchmarks made on a MacBook Pro, Core i7 2.66 GHz with an array of typo real.Times are expressed in milliseconds.

 

Count of elements Sum DoSumArray
1,000 0 3
5,000 0 13
10,000 0 24
100,000 0 240
1,000,000 2 2,426

 

It was worth it, right? Just in case somebody in the audience isn't convinced, here are the same results as a graph. You can see how, with the call to Sum, the speed is almost constant, even immeasurable in milliseconds:

 

 

By the way – even if it does not strictly fit the scope of this article – here is a way to optimize your DoSumArray generic method. The idea is to avoid using a pointer, since for every iteration of the loop, 4D needs to de-reference the pointer, which may be time-consuming (when done millions of times).

 

To speed up this code, you should:

 

    (1) Duplicate in a local array the array received in $1, then
    (2) loop on this local array

 

Here is the code: 

 

// Method $result:=DoSumArrayBoosted(->anArray)

C_POINTER($1)

C_REAL($0)

C_LONGINT($i;$L_type)

 

$0:=0

$L_type:=Type($1->)

Case of 

  : ($L_type=Real array)

    ARRAY REAL($rF_temp;0)

    COPY ARRAY($1->;$rF_temp)

    For ($i;1;Size of array($rF_temp))

        $0:=$0+$rF_temp{$i}

    End for 

 

  : ($L_type=LongInt array )

    ARRAY LONGINT($rL_temp;0)

    COPY ARRAY($1->;$rL_temp)

    For ($i;1;Size of array($rL_temp))

        $0:=$0+$rL_temp{$i}

    End for 

 

  : ($L_type=Integer array)

    ARRAY INTEGER($rI_temp;0)

    COPY ARRAY($1->;$rI_temp)

    For ($i;1;Size of array($rI_temp))

        $0:=$0+$rI_temp{$i}

    End for 

 

End case 

And now, the results:

 

Count of elements DoSumArray DoSumArrayBoosted
1,000 3 1
5,000 13 8
10,000 24 17
100,000 240 174
1,000,000 2,426 1,751

 

Starting at 10,000 elements, the boosted version is around 30% faster than the original. If you care about memory, keep in mind that a real array with ten million elements has a size of 80 MB. This must be taken into account. You could optimize your optimization(5), and use $1 or the copy (or both) depending on he count of elements. Of course, un any case, even the boosted version is still way slower than a direct call to Sum. I was suggesting this trick just for you to use until you move to v13.

 

In 4D v13, therefore, you should graduallly replace - although there is no rush - your loops that perform statistical operations on arrays with calls to the corresponding 4D command.

 

These optimizations in v13 go far beyond the "On a Series" theme of commands. For example, take a look at COPY DOCUMENT. You can now duplicate an entire folder, with all its content. Again, you may already have needed to do such a thing and have already written some code, more or less complex, looping on folders, on files in folders, in folders and files in folders in folders… Hello v13! Just COPY DOCUMENT(sourceFolder;destFolder), and 4D does it for you. Fast. Also, take a look at CREATE FOLDER, which can now create the missing folders in the path. You also have DOCUMENT LIST which now can return – among other things – the list of all the documents, recursively.

 

Using a single line of code – the call to the 4D command – not only simplifies your code, but also brings performance benefits. Another example: The commands of the new HTTP Client theme. They deserve several blogs for themselves. Replace your billions of lines using Internet Commands with just one, HTTP Get. It even handles authentication and proxies.
 

Pro-du-cti-vi-ty!

 

(1) Obviously
(2) Guess its command number. 1. It's the first command that has been implemented in 4D.
(3) Or sub-selection of subrecords. But you don't have subtables in your structure.
(4) "Fewer lines, fewer bugs." Even if, in this specific case, your generic code has been running perfectly for decades.

(5) The kind of expression I'm able to provide. Anytime. Just ask.