There was an app

Wanted to make a separate more detailed post of my previous post item about how the order of things may greatly influence the time performance of code.

There is an app which reads a large text file to find out how many unique words it has. The app also reports on the top 100 unique words in the file, and the count of them.

One file had 2 735 307 words, of which unique were 99 130.

Since the programmer was clever, s/he used a binary search tree to do the job of keeping book of the unique words. Then it transferred the job to a table to sort the words in order of frequency to print out the top-100 list.

Inserting words into the binary search tree, or adding the appearance (frequency) counter of the word if already in the tree.

When measuring the time performance of the app to handle this one file, the app reported that it could do the job in 2.550396 seconds*).

When looking at the code inserting the word to the tree (above) closely, one can see that a node is allocated and then free’d even when the word is already in the tree, without anything useful done with the newnode. The node is needed only when the word is not already in the tree.

So, why not move the allocation of the node after the loop, when the node is actually needed:

Allocating the newnode only when it is actually necessary.

This small, some may say a pedantic change, took off 30% of the execution time of the whole app, when handling that same file mentioned above. After this change, the execution time of the app dropped down to 1.867050 seconds. Repeated runs produced similar timings for both solutions.

Allocating from the heap, done repeatedly, is slow.

*) Measurements done on Apple Mac Mini M1 2020, 16Gb RAM, 8 cores, 1 Tb SSD.

Today’s thoughts

Someone mentioned the book Exercises in Programming Style by Cristina Lopes in Twitter and I bought it. Interesting read, can recommend. I can bring some things from the book into my course next Fall. We should actually have a course focusing mainly on these topics but alas, do not have one. I just need to implement those things I could apply in Java, maybe as demonstrations, since the book is in Python.

Another thought I need to embed into course material is this nice example of how order of things may greatly influence the time performance of code. I was evaluating a student submission of a course project. A function added nodes (unique word counts of very large text files) into a binary search tree. First a node was allocated using malloc after which the tree was searched in a loop where to put the node. And if a node with the value already existed, the frequency count of the existing node was increased and the node just created could be freed and function would return. And if the value of the node was not found in the tree, then the node just allocated was inserted into the tree in the place that was found for it. By just moving the malloc after the loop (where the node was actually needed) got rid of 30% of the execution time of the whole app. Boom! What an improvement with such a small change. Which is obvious if you have some experience but not so for many students who are in the beginning. This will be a fun demonstration…

Final thought of the day popped to my mind because of that minor piece of code having a large effect, and that is this saying:

The idiom “the straw that broke the camel’s back“, alluding to the proverb “it is the last straw that breaks the camel’s back”, describes the seemingly minor or routine action that causes an unpredictably large and sudden reaction, because of the cumulative effect of small actions.

Wikipedia