#2905 Naive Concatenation vs StrBuf

Gary Sun 28 Jan

I just have to hand it to the creators of Fantom. So far, I find Fantom to have one of the most beautiful syntax. Since we were discussing naive string concatenation vs StrBuf, I felt inspired to quantitatively compare the two in Fantom, while continuing to learn. This is what I've come up with. It compiles and it works as expected. Although I'm sure there are some tweaks that can be made. TODO: I would like to add a method to calculate how much faster StrBuf is compared to concatenating.

class AddTwoArrays3 {
    static Void main() {
        Int workload := 100_000  // Adjust the workload as needed
        echo("Workload: " + workload)

        // Naive string concatenation
        DateTime startMillis := DateTime.now
        Duration startNano := Duration.now
        Str s := ""
        (1..workload).each |Int i| { s = s + i.toStr }
        DateTime endMillis := DateTime.now
        Duration endNano := Duration.now
        printTime("Naive concatenation time", endMillis - startMillis, endNano - startNano)

        // StrBuf (equivalent to StringBuilder)
        startMillis = DateTime.now
        startNano = Duration.now
        StrBuf sb := StrBuf()
        (1..workload).each |Int i| { sb.add(i.toStr) }
        endMillis = DateTime.now
        endNano = Duration.now
        printTime("StrBuf time", endMillis - startMillis, endNano - startNano)
    }

    private static Void printTime(Str label, Duration millis, Duration nanos) {

    Float nanoSeconds := nanos.ticks / 1_000_000_000.0f   
    Float milliSeconds := nanoSeconds * 1000

    echo("$label (nanoseconds): $nanos")
    echo("$label (milliseconds): ${milliSeconds}ms")
    echo("$label (seconds): ${nanoSeconds}s")

    }
}

OUTPUT:

Workload: 100000
Naive concatenation time (nanoseconds): 8515165367ns
Naive concatenation time (milliseconds): 8515.165367ms
Naive concatenation time (seconds): 8.515165367s
StrBuf time (nanoseconds): 5339082ns
StrBuf time (milliseconds): 5.339082ms
StrBuf time (seconds): 0.005339082s

SlimerDude Sun 28 Jan

Hi Gary, I'm pleased you're enjoying your investigation so far!

As mentioned, Fantom's StrBuf is a wrapper around Java's StringBuilder - if interested you can see the src for StrBuf here. (The entire sys pod is written in Java.)

So your comparison is actually Java String concatenation versus Java StringBuilder concatenation. There's plenty on the web with regards to this if you'd like to read more; for example, here's one such article:

Java String concatenation versus Java StringBuilder concatenation

https://dev.to/this-is-learning/performance-benchmarking-string-and-string-builder-3bid

Gary Mon 29 Jan

Thank you Steve for the links - very interesting stuff! Funny you should mention Java because I actually wrote the program first in Java because I'm somewhat more familar with Java syntax than Fantom's (especially the functional syntax). I had AI help translate some of the code - but it got much of it hilariously wrong. So through trial and error and reading over the documentation, I was able to piece together what appeared reasonable and that compiled.

Here's the Java code used as a launching platform for Fantom:

public class NaiveVsStrBuilder {
public static void main(String[] args) {
        int workload = 100_000;  // Adjust the workload as needed

        // Naive string concatenation
        long startMillis = System.currentTimeMillis();
        long startNano = System.nanoTime();
        String s = "";
        for (int i = 1; i <= workload; i++) {
            s = s + i;
        }
        long endMillis = System.currentTimeMillis();
        long endNano = System.nanoTime();
        printTime("Naive concatenation time", endMillis - startMillis, endNano - startNano);

        // StringBuilder
        startMillis = System.currentTimeMillis();
        startNano = System.nanoTime();
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= workload; i++) {
            sb.append(i);
        }
        endMillis = System.currentTimeMillis();
        endNano = System.nanoTime();
        printTime("StringBuilder time      ", endMillis - startMillis, endNano - startNano);
    }

    private static void printTime(String label, long millis, long nanos) {
        System.out.println(label + " (milliseconds): " + millis);
        System.out.println(label + " (nanoseconds): " + nanos);
        System.out.printf("%s (seconds): %.3f%n ", label, millis / 1000.0);
    }
}
Naive concatenation time (milliseconds): 2371
Naive concatenation time (nanoseconds): 2371256585
Naive concatenation time (seconds): 2.371
StringBuilder time       (milliseconds): 4
StringBuilder time       (nanoseconds): 3631343
StringBuilder time       (seconds): 0.004

pcl Thu 1 Feb

Thanks for posting this @Gary. I have always used StrBuf out of a general awareness that it was "more efficient", but I never appreciated just how much difference it could make!

Gary Sun 11 Feb

@pcl my pleasure! It is fascinating stuff! :->

Login or Signup to reply.