Development Sandbox: Int to String Conversion

Recently, while reviewing some legacy code I discovered a trick a developer had used to convert an integer to a string. The developer did this because the function only accepted a string as a parameter. As developers, often we look for succinct ways to code thinking that less code is faster. The interesting thing about the code is that it was the slowest of all the tests I had done. In all my efforts, I found five different ways to code to a conversion of an integer to a string. Continue reading “Development Sandbox: Int to String Conversion”

Development Sandbox: String Concatenation

When you review and work with other people’s code you sometimes find some tricks to optimize your own code. Most of the time the tricks look impressive in their succinctness and streamlined approach. And so you’d assume that the performance behind the scenes would be mind-blowing. So I decided to have a look at string concatenation. I have seen a number of ways to concatenate strings. Usually, the ones that do everything within scope will do it properly. And there are concatenations done other ways for appropriate reasons. But I thought I’d still have a look at the many ways one can concatenate a string.

Continue reading “Development Sandbox: String Concatenation”

Development Sandbox: Java code inlining

Once in a while, it’s fun to take out the Java development tools and experiment. So I did just that. I went online and found a bytecode editor that does a quick job of parsing Java class files. I wanted to see what my code looked like in bytecode form. I wanted to test if a complicated if condition would get inlined. You know what I’m talking about. You’ve written that complicated condition fairly often. You need to validate that the variable you’re testing against is not null and is accessible.

Below is an example of such a condition:

arrayOfValues != null && arrayOfValues.length > 0 && arrayOfValues[0].equals(Integer.toString(anotherValue))

When a condition gets ugly I will often extract it into its own method to make the code easier to read. It also provides the opportunity to do some intense unit testing.

So here is the sample code I wrote that I would be testing:

public class Sample {
    // The number of iterations for the for loop
    static int iterations = 1000000;
    // How often do we print the iteration
    static int step = 100000;

    // The variable in our test
    static String[] arrayOfValues = {"1353"};

    public static void main(String... args) {
        // This is the big loop
        for (int i = 0; i < iterations; i++) {

            // Notify us of where we are in the loop
            if (i % step == 0) {
                System.out.println("Iteration: " + i);
            }

            // Notify us when we've match with the iteration
            if (isAMatch(i)) {
                System.out.println("Found: " + i);
            }
        }
    }

    // This is our test condition
    private static boolean isAMatch(int i) {
        return arrayOfValues != null && arrayOfValues.length > 0 && arrayOfValues[0].equals(Integer.toString(i));
    }
}

The code isn’t complicated so I won’t explain what it’s doing. But note the extracted method isAMatch() it’s an example of what most developers would code.

By default, the JIT will inline methods that are 34 bytes or shorter. So I used the Java Bytecode Editor to review the class file and see what’s what. According to the editor the function isAMatch() is just about 34 bytes long; just within the maximum limit. I’ll be honest, I thought Java would be able to fit more within 34 bytes. In a later post, I’ll experiment with different ways of writing the condition to see if I can get it shorter. But for now, it seems that the function is a good candidate for inlining.

javabytecodeeditor-1

On to testing

I compile the code using the Java 8 JDK with the following statement. Nothing extravagant.
javac Sample.java
The fun starts when executing the application, that’s when the JIT does all of its optimization magic. Below is the command I used to execute the class file.
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining Sample
This is what those options do:
  • UnlockDiagnosticVMOptions: This option it turns on the diagnostic tracking. Oddly enough, it is not listed on Oracle’s website even though it is required for PrintInlining to work. But the JIT will display an error if it’s not provided.
  • PrintInlining: This option will display a report on all the decisions the JIT makes in regards to inlining methods. It is impressive to see in action.
When you execute the class this is the output that you’ll get.

 

</pre>
@ 1 java.lang.Object::<init> (1 bytes)
@ 13 java/lang/StringIndexOutOfBoundsException::<init> (not loaded) not inlineable
@ 30 java/lang/StringIndexOutOfBoundsException::<init> (not loaded) not inlineable
@ 65 java/lang/StringIndexOutOfBoundsException::<init> (not loaded) not inlineable
@ 75 java.util.Arrays::copyOfRange (63 bytes) callee is too large
@ 16 java.lang.StringBuilder::<init> (7 bytes)
@ 3 java.lang.AbstractStringBuilder::<init> (12 bytes)
@ 1 java.lang.Object::<init> (1 bytes)
@ 20 java.lang.StringBuilder::append (8 bytes)
@ 2 java.lang.AbstractStringBuilder::append (62 bytes) callee is too large
@ 25 java.lang.StringBuilder::append (8 bytes)
@ 2 java.lang.AbstractStringBuilder::append (50 bytes) callee is too large
@ 29 java.lang.StringBuilder::append (8 bytes)
@ 2 java.lang.AbstractStringBuilder::append (62 bytes) callee is too large
@ 32 java.lang.StringBuilder::toString (17 bytes)
@ 13 java.lang.String::<init> (82 bytes) callee is too large
@ 35 java.lang.IllegalArgumentException::<init> (6 bytes) don't inline Throwable constructors
@ 54 java.lang.Math::min (11 bytes)
@ 57 java.lang.System::arraycopy (0 bytes) intrinsic
@ 66 java.lang.String::indexOfSupplementary (71 bytes) callee is too large
@ 18 java/lang/StringIndexOutOfBoundsException::<init> (not loaded) not inlineable
@ 4 java.lang.CharacterDataLatin1::getProperties (11 bytes)
@ 1 java.lang.Character::toUpperCase (9 bytes)
@ 1 java.lang.CharacterData::of (120 bytes) callee is too large
@ 5 java.lang.CharacterData::toUpperCase (0 bytes) no static binding
@ 1 java.lang.CharacterData::of (120 bytes) callee is too large
@ 5 java.lang.CharacterData::toUpperCase (0 bytes) no static binding
@ 1 java.lang.CharacterData::of (120 bytes) callee is too large
@ 5 java.lang.CharacterData::toLowerCase (0 bytes) no static binding
@ 4 java.lang.CharacterDataLatin1::getProperties (11 bytes)
@ 3 java.lang.String::indexOf (70 bytes) callee is too large
@ 17 java.lang.AbstractStringBuilder::newCapacity (39 bytes) callee is too large
@ 20 java.util.Arrays::copyOf (19 bytes)
@ 11 java.lang.Math::min (11 bytes)
@ 14 java.lang.System::arraycopy (0 bytes) intrinsic
@ 7 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
@ 17 java.lang.AbstractStringBuilder::newCapacity (39 bytes) callee is too large
@ 20 java.util.Arrays::copyOf (19 bytes)
@ 11 java.lang.Math::min (11 bytes)
@ 14 java.lang.System::arraycopy (0 bytes) intrinsic
@ 2 java.lang.AbstractStringBuilder::append (29 bytes)
@ 7 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
@ 17 java.lang.AbstractStringBuilder::newCapacity (39 bytes) callee is too large
@ 20 java.util.Arrays::copyOf (19 bytes)
@ 11 java.lang.Math::min (11 bytes)
@ 14 java.lang.System::arraycopy (0 bytes) intrinsic
@ 7 java.lang.AbstractStringBuilder::append (29 bytes)
@ 7 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
@ 17 java.lang.AbstractStringBuilder::newCapacity (39 bytes) callee is too large
@ 20 java.util.Arrays::copyOf (19 bytes)
@ 11 java.lang.Math::min (11 bytes)
@ 14 java.lang.System::arraycopy (0 bytes) intrinsic
@ 5 java.lang.AbstractStringBuilder::appendNull (56 bytes) callee is too large
@ 10 java.lang.String::length (6 bytes)
@ 21 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
@ 17 java.lang.AbstractStringBuilder::newCapacity (39 bytes) callee is too large
@ 20 java.util.Arrays::copyOf (19 bytes)
@ 11 java.lang.Math::min (11 bytes)
@ 14 java.lang.System::arraycopy (0 bytes) intrinsic
@ 35 java.lang.String::getChars (62 bytes) callee is too large
@ 2 java.lang.AbstractStringBuilder::append (50 bytes) callee is too large
Iteration: 0
@ 1 java.lang.Object::<init> (1 bytes)
@ 19 Found: 1353 java.lang.Integer::toString (48 bytes) callee is too large

@ 22 java.lang.String::equals (81 bytes) callee is too large
@ 15 java.lang.Integer::stringSize (21 bytes)
@ 24 java.lang.Integer::stringSize (21 bytes)
@ 35 java.lang.Integer::getChars (131 bytes) callee is too large
@ 44 java.lang.String::<init> (10 bytes)
@ 1 java.lang.Object::<init> (1 bytes)
@ 24 java.lang.Integer::stringSize (21 bytes) inline (hot)
@ 35 java.lang.Integer::getChars (131 bytes) inline (hot)
@ 44 java.lang.String::<init> (10 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 19 java.lang.Integer::toString (48 bytes) inline (hot)
@ 24 java.lang.Integer::stringSize (21 bytes) inline (hot)
@ 35 java.lang.Integer::getChars (131 bytes) inline (hot)
@ 44 java.lang.String::<init> (10 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 22 java.lang.String::equals (81 bytes) (intrinsic)
@ 24 java.lang.StringBuilder::<init> (7 bytes)
@ 3 java.lang.AbstractStringBuilder::<init> (12 bytes)
@ 1 java.lang.Object::<init> (1 bytes)
@ 29 java.lang.StringBuilder::append (8 bytes)
@ 2 java.lang.AbstractStringBuilder::append (50 bytes) callee is too large
@ 33 java.lang.StringBuilder::append (8 bytes)
@ 2 java.lang.AbstractStringBuilder::append (62 bytes) callee is too large
@ 36 java.lang.StringBuilder::toString (17 bytes)
@ 13 java.lang.String::<init> (82 bytes) callee is too large
!m @ 39 java.io.PrintStream::println (24 bytes)
@ 6 java.io.PrintStream::print (13 bytes)
!m @ 9 java.io.PrintStream::write (83 bytes) callee is too large
!m @ 10 java.io.PrintStream::newLine (73 bytes) callee is too large
@ 43 Sample::isAMatch (34 bytes)
@ 19 java.lang.Integer::toString (48 bytes) callee is too large
@ 22 java.lang.String::equals (81 bytes) callee is too large
@ 56 java.lang.StringBuilder::<init> (7 bytes)
@ 3 java.lang.AbstractStringBuilder::<init> (12 bytes)
@ 1 java.lang.Object::<init> (1 bytes)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 61 java.lang.StringBuilder::append (8 bytes)
@ 2 java.lang.AbstractStringBuilder::append (50 bytes) callee is too large
@ 65 java.lang.StringBuilder::append (8 bytes)
@ 2 java.lang.AbstractStringBuilder::append (62 bytes) callee is too large
@ 68 java.lang.StringBuilder::toString (17 bytes)
@ 13 java.lang.String::<init> (82 bytes) callee is too large
!m @ 71 java.io.PrintStream::println (24 bytes)
@ 6 java.io.PrintStream::print (13 bytes)
!m @ 9 java.io.PrintStream::write (83 bytes) callee is too large
!m @ 10 java.io.PrintStream::newLine (73 bytes) callee is too large
@ 15 java.lang.Integer::stringSize (21 bytes) inlining prohibited by policy
@ 24 java.lang.Integer::stringSize (21 bytes) inlining prohibited by policy
@ 35 java.lang.Integer::getChars (131 bytes) callee is too large
@ 44 java.lang.String::<init> (10 bytes)
@ 1 java.lang.Object::<init> (1 bytes)
@ 19 java.lang.Integer::toString (48 bytes) callee is too large
@ 22 java.lang.String::equals (81 bytes) callee is too large
@ 24 java.lang.StringBuilder::<init> (7 bytes)
@ 3 java.lang.AbstractStringBuilder::<init> (12 bytes)
@ 1 java.lang.Object::<init> (1 bytes)
@ 29 java.lang.StringBuilder::append (8 bytes)
@ 2 java.lang.AbstractStringBuilder::append (50 bytes) callee is too large
@ 33 java.lang.StringBuilder::append (8 bytes)
@ 2 java.lang.AbstractStringBuilder::append (62 bytes) callee is too large
@ 36 java.lang.StringBuilder::toString (17 bytes)
@ 13 java.lang.String::<init> (82 bytes) callee is too large
!m @ 39 java.io.PrintStream::println (24 bytes)
@ 6 java.io.PrintStream::print (13 bytes)
!m @ 9 java.io.PrintStream::write (83 bytes) callee is too large
!m @ 10 java.io.PrintStream::newLine (73 bytes) callee is too large
@ 43 Sample::isAMatch (34 bytes)
@ 19 java.lang.Integer::toString (48 bytes) callee is too large
@ 22 java.lang.String::equals (81 bytes) callee is too large
@ 56 java.lang.StringBuilder::<init> (7 bytes)
@ 3 java.lang.AbstractStringBuilder::<init> (12 bytes)
@ 1 java.lang.Object::<init> (1 bytes)
@ 61 java.lang.StringBuilder::append (8 bytes)
@ 2 java.lang.AbstractStringBuilder::append (50 bytes) callee is too large
@ 65 java.lang.StringBuilder::append (8 bytes)
@ 2 java.lang.AbstractStringBuilder::append (62 bytes) callee is too large
@ 68 java.lang.StringBuilder::toString (17 bytes)
@ 13 java.lang.String::<init> (82 bytes) callee is too large
!m @ 71 java.io.PrintStream::println (24 bytes)
@ 6 java.io.PrintStream::print (13 bytes)
!m @ 9 java.io.PrintStream::write (83 bytes) callee is too large
!m @ 10 java.io.PrintStream::newLine (73 bytes) callee is too large
@ 24 java.lang.Integer::stringSize (21 bytes) inline (hot)
@ 35 java.lang.Integer::getChars (131 bytes) inline (hot)
@ 44 java.lang.String::<init> (10 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
Iteration: 100000
Iteration: 200000
@ 24 java.lang.StringBuilder::<init> (7 bytes) inline (hot)
@ 29 java.lang.StringBuilder::append (8 bytes) inline (hot)
@ 33 java.lang.StringBuilder::append (8 bytes) executed < MinInliningThreshold times
@ 36 java.lang.StringBuilder::toString (17 bytes) executed < MinInliningThreshold times
!m @ 39 java.io.PrintStream::println (24 bytes) executed < MinInliningThreshold times
@ 43 Sample::isAMatch (34 bytes) inline (hot)
@ 19 java.lang.Integer::toString (48 bytes) inline (hot)
@ 24 java.lang.Integer::stringSize (21 bytes) inline (hot)
@ 35 java.lang.Integer::getChars (131 bytes) inline (hot)
@ 44 java.lang.String::<init> (10 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 22 java.lang.String::equals (81 bytes) (intrinsic)
Iteration: 300000
Iteration: 400000
@ 19 java.lang.Integer::toString (48 bytes) inline (hot)
@ 24 java.lang.Integer::stringSize (21 bytes) inline (hot)
@ 35 java.lang.Integer::getChars (131 bytes) inline (hot)
@ 44 java.lang.String::<init> (10 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 22 java.lang.String::equals (81 bytes) (intrinsic)
Iteration: 500000
Iteration: 600000
Iteration: 700000
Iteration: 800000
Iteration: 900000

What I noticed is that the method isAMatch() is not inlined until it has gone through at least 200,000 iterations as noted by the label inline (hot) next to the method isAMatch() on line 160. To me that says three things.
First, the JIT thinks the method executes fast enough that it didn’t warrant inlining the first 200,000 iterations.
Second, a lot of smaller methods are inlined before the code reaches 100,000 iterations.
Third, I’m guessing that smaller methods are inlined first simply due to overhead in calling them. The overall performance of the application is what’s most important. So JIT thought that my method could wait.

It’s important to note that there are JIT options to control the maximum byte size of methods to inline. What I would like to investigate is how inlining plays out with regards to natively compiled code. And when does the JIT think it necessary to compile bytecode to native code. All fun stuff.  So stay tuned for more on this.

The Best Scala Programming Resources

In the last few weeks that I’ve spent learning Scala I’ve discovered several amazing online resources. So much so that it made it difficult to justify purchasing a book, even though I did buy the much-recommended Programming in Scala title. Every time I needed help in figuring out how to do something, the following websites were indispensable.

Some of these sites are no longer active, but the information they provide is still very much relevant to new and experienced Scala programmers. Continue reading “The Best Scala Programming Resources”

Four reasons to try Scala and love it

I’ve been working a lot with Scala in the last few weeks. It is an amazing programming language, to say the least. It’s terse, intuitive, unambiguous. And a real treat when you want to do stuff. It’s the programming language of the future. If only it would get more support and momentum going for it. Plus, the quantity of online resources available is just mind boggling.

Below are my N reasons why Scala is the best JVM language. Continue reading “Four reasons to try Scala and love it”

Good programmers should have great math skills

Coding exercises are one of the best ways to improve your programming skills. In recent weeks, I’ve done several exercises on a couple of online websites. Those being CodinGames and HackerRank; you can follow my progress here and here. Coding exercises are great for improving your coding skills. But it’s also a chance to spend time in minimising the code you generate.

I’ve been focusing on learning Scala, a JVM-based function programming language. It’s a magnificent language with a rich streaming library perfect for this sort of thing. But as anyone who has done any of these challenges, it often involves a lot of algebra. And sometimes trigonometry. Although I’m okay at assembling my own formulas for solving problems. But solving for x can sometimes be a tad challenging when the formula is a bit complex. Continue reading “Good programmers should have great math skills”

Edit and execute programming code online quick

Ever wish you could test a snippet of code? Online. Like now! Or focus on improving an algorithm in isolation. Or have a moment of clarity and need to write a piece of code lickety-split. Yesterday, I found this online tool to do just that. That’s good for me, as I don’t have to create a whole new project in IntelliJ IDEA or use the CLI tools.

CodePad supports several of the most used programming languages. Unfortunately, it doesn’t support Haskell, a language I’d like to tackle this year. But for Scala, it’s really good.

The tool itself is easy to use and the compiler quick enough. In my case, I was using it to optimise a piece of Scala code to submit for an online coding exercise.

codepad

Website: https://codepad.remoteinterview.io