So, today's presentation is going to be focused on that day made transfer, that you see on the left hand side here.
We're trying to figure out how fast it is to run a string chart.
So, today we're doing with two types of string, a string that is Latin one.
So, it's standard string with just 8 to 8 to set characters.
And the only one is a UTF-16 string.
And for today, what we want to also remember a little bit how string chart works.
The way works is called a basic check to see where this is a Latin one.
If it is, it delegates to a string Latin one throughout, otherwise the string UTF-8,
where its character is two bytes instead of one.
Something that might be worth remembering is how string Latin one throughout works.
All it does really check the index is the index within the boundaries of the string that I'm trying to locate,
or the character I'm trying to locate.
And then, all it does is track the characters and it's simple.
A string UTF-8 chart is very similar.
It's a little more complicated.
We got two characters per string, but for today, it's not so important.
But it's important to remember how this works.
The string Latin one throughout.
And what you see on the right hand side, that is the code that damage generates for an average time benchmark.
So, basically, it wraps your own benchmark around some code,
where it starts by taking the start time,
then it goes into a loop, and invokes your method all the time,
but the number of operations until it's done.
It's done.
It's basically a Boolean variable here.
A Boolean, a volatile Boolean.
And then it calculates a stop time.
It's got the time, it's got the operations pump.
It can correct simple.
So, we're going to do next, it'll just run it.
So, let's go here.
This is big enough.
So, let's run this benchmark.
We're going to run it in a slightly noble way,
noble, because this is the first time
that I'm showing this to the outside world.
What you see is, we're benchmarking
a global VM, native image.
We basically, before this talk,
I wrapped the benchmark into a global VM native image.
And what I'm doing here is I'm basically
invoking the native binary, which is a target on benchmarks.
And you can see from the VM version that there is some cruise here.
We ran on a substrate VM.
So, this is not hotspot.
This is the VM, the runs, global VM native images.
And we see we're running global VM community addition.
This is the first time this is showing life outside of my team.
And we can in some numbers, okay?
So, here at the bottom you can see some numbers.
Let me put them right away at higher up.
So, there is a few questions we can ask about these numbers.
First one, are these numbers fast or the slow?
That's a little bit hard for us to know,
because first, you don't know what the specs of this machine are.
And second, you don't have any point of reference to compare with.
And the other question is, you might want to follow my.
So, let's go to the first.
Follow my, I'm an engineer at the Open JDK team at Red Hat.
I work on global VM native image.
And also hotspot, git compilers.
And for today, the most important thing is I am the creator of the
extension that you're seeing in action there.
What it allows is to do the same extension.
This is essentially benchmark yabba code when it's running inside
a global VM native image.
So, let's go back to our numbers.
What we wanted to answer first is where this was fast or slow.
But before we get there, there's something a little bit not going on here.
We've got lotting one being slower the UTF-A16 by quite a bit.
And this is slightly surprising.
If anything, this should be roughly about the same in performance.
One, but expect.
If anything, one would expect maybe UTF-A16 to be a slightly slower,
because it's a more complex implementation.
But this is surprising.
So, what do we do?
Let's profile it.
How can we profile it?
We can profile it, but who can in a profiler.
So, this profiler here, do you see?
This is one that we've created specifically for this work, for the
global VM native image.
What it does, it wraps the day-mage
invocation into the native invoker here,
around a perfect code invocation.
And we added in the call graph parameter.
In order to use the dual of debugging for symbols available for
native images, in order to then extract,
to be able to match what it is,
they are simply with what code we're running.
The numbers here are not so important.
The most important thing is that out of each benchmark,
we get a pair of binary output here.
And then what we can do is we can expect it.
How can we expect it?
We can basically go Perf, annotate,
and we're going to open it.
Can you see that the bottom here,
and basically going to go.
I'm going to start with Latin one.
Now, we're going to see some assembly.
I'm not going to go into a lot of depth.
I've come back up slides explaining things in
quality detail.
I'm going to try to understand a little bit the flow of what's going on
when we go here.
So, what we see is that for the Latin one,
it tells us that the first method it jumps to is the
string Latin one chart.
Okay, we say earlier, this is the method I get
called for a string chart.
Okay, this is the implementation.
We see that there's a check in this call.
This is the call that we saw earlier.
It's actually not exactly the same call,
but it's one that is on the Nithar.
And that's pretty much what we need to know at this stage.
We can move around a little bit.
Here, what we see now, this is the actual game
that is calling in to our chart benchmark.
Good.
And then here, what we see here is the string.
No, this is...
Oh, it's been something interesting.
It has happened here.
Oh, yeah.
No.
This is our string chart.
A chart Latin, which is calling is to a string chart.
And then here's what I wanted to get to.
This is basically a string chart.
The basically, what is doing is the most important thing here
is calling this to a string Latin one chart.
Okay, so we see, I'll leave it the chain of all the calls.
And I'll leave it a slightly different way,
but we see our benchmark calling the string chart.
String chart, calling to a string Latin one chart, et cetera.
Okay.
That's how it works for Latin one.
What about UTF-A16?
Okay.
So let's go here.
Where are we?
We're in a string chart.
Okay.
So what do we have down here?
We've seen a lot of knobs, a lot of things.
We've got the tech index.
But there's nothing else.
So what you see in the screen is that the string UTF-A16
where our implementation has been inline is to a string chart.
So we can make a theory here.
I can make a theory saying the reason why UTF-A16
was performed in better the string Latin one was because
UTF-A16 chart was inline in the string chart.
Okay.
That's a theory I have.
Now, how can I prove it?
I can prove it.
Let's prove it.
What can we do?
We're going to rebuild a native binary.
Are we going to rebuild this passing in a parameter called
max notes in trivial method 40?
Okay.
So let me make a theory.
What we doing?
This parameter here.
Why are those bills?
I'm going to explain what's going on.
The call compiler will inline one of the conditions for inline
in a method is when a method is considered trivial.
What does it mean to be for a method to be trivial?
It means that inside the method the compiler graph has 20 notes
or less.
So what I'm doing here is something in the case.
Go and inline.
Those methods have got 40 notes instead of 20.
Okay.
So I'm basically giving in more budget to inline bigger methods.
And we're going to see what happens then when we do that.
So this is native image.
So it does the bill.
And then basically eventually comes on to the button.
Okay.
We got you running.
Yes.
Run edge.
See what we see now.
Let's start again.
We see our benchmark is running.
It's of trivial.
Well, we can see at the top.
It's like a benchmarks.
We start to see numbers.
We see numbers are considerably faster than we saw before.
But the interesting thing we're going to see now is we're going to see
the numbers between UTF-16 and LAT-1 are pretty much the same now.
So I feel we seem to have legs.
The reason why things improved was because of inline in.
What we can go is the further.
We can look at the profiling data.
Obviously, I'm not going to go through entire steps because I only got 20 minutes.
But we're going to look at it directly here.
So we're going to do profanoid.
Well, sorry, if I can type.
Well, now we see a string chart for LAT-1.
We basically go into the profiling data for LAT-1.
And we see a string chart.
We see the jump.
This is probably the jump for the coder.
And then we jump down to here.
And then we see this number of techniques.
Check index is gone.
The call.
We also go now a string LAT-1 calls anymore.
So we can see inlining how successfully happened.
And here is basically the final instruction.
This is the instruction.
The moves it'll.
It's a move with a zero.
Zero.
Basically, it's converted the bite into a chart.
In a way that that then can be returned.
So we answer one question.
Why UTF-A-16 was faster than LAT-1.
We have, I post another question earlier.
Which is, is that those numbers we saw earlier,
even the ones here, are they faster or slow?
And obviously, what we can do is a very simple thing is,
how do things allow with hotspot with standard JDK?
So let's do that.
So we're going to package it.
We're going to package it in JV mode.
Great.
Now we're going to run it.
And we're going to focus now.
We're going to focus on the LAT-1.
Okay?
We're going to leave UTF-A-16 aside.
And we're also going to do, we're going to add a profiler.
Perfect.
That allows us to see what the assembly looks like for this particular case.
So we start running.
Obviously, the VM version has changed.
So that you can see it here.
We now have up in JVK version, the invoker is Java.
So this is a clear difference with what we're doing before.
And we start to see numbers.
We see we got 1.7 nanosecond preparation.
So which is faster than LAT.
Okay?
That's not all of news.
I mean, it's something that we would all expect that to happen.
AOT can make the same optimizations as a hospital can do.
Or that's it.
Well, let's have a look.
And I'm going to make this a slightly smaller.
Just that it's been more clear.
So the way it's going to be a little bit confusing.
Maybe hopefully not.
Okay.
That's a big enough.
Once again, I'm not going to try to go into a lot of detail.
What we can see is that the first thing is that the HOTS method is the JMHNAT.
It's called.
In the very last one, we saw the string chart was the HOTS method.
So obviously, the inline in that we achieved with the previous option.
But we increased the budget to 40 notes.
It's not as good as the inline in the G, the HOTS population can do.
Obviously, HOTS can see what is HOTS and can basically optimize the inline in.
So we got a lot more inlining happening here.
And one of the things that is interesting to see as well is that,
essentially what we see is this is a lot of inline assembly.
And then we get back to the bottom.
And then we basically after this there is a loop back at which we don't see it here.
Sometimes you see, sometimes you see it, but you see it as a loop back up.
So basically we run my iteration, we loop back up.
Now, I have one more thing to show you today.
AOT, we all expected that was going to be slower than yet.
What about gravity and peak yield?
So gravity and peak yield is a proprietary technology for Oracle.
But it allows you to, basically it's called profile guided optimization.
The idea is you run your native binary with some training,
with some instrumentation.
Then you run it through your training or your benchmark or whatever.
And then you out of that you get some profile in data.
You tend to profile in data.
You use it to rebuild your native image with this data.
And you basically have got something akin to a kit.
But basically you've done an offline with just some training.
The question is, would that be faster than a hotspot or not?
Who thinks this is going to be a hotspot that is going to be faster than peak yield?
Can you raise your hands?
Two people. Who thinks peak yield is going to be faster than a hotspot?
Six or seven. Okay.
No, no, no participation. Let's have a look.
So here we have already have prevailed things ahead of this code today or ahead of this presentation.
So let's run it.
This is running a slightly different now.
Well, the first notice of what difference is the VM version has changed.
When you're running with Oracle GraphiM, that's a proprietary version.
The VM in Boga has a slightly changed.
But the changes that you see in the screen about the VM Boga name is not so relevant.
But it's relevant is that this VM in Boga initially is a p-year instrumentative vocar.
What we do is behind the scenes, we inject a warmer fork so that it runs on the instrumentative binary.
Then when that completes, we take the profiling data that comes out of the instrumentation and we rebuild the native image with that,
which is what's happening right now here.
When that completes, we basically execute the benchmark with the optimist native binary.
And we start to see the numbers.
And we see it takes about 1.4 nanosecosperification.
Well, we focus on Latin one.
So I leave the aside the UTF-60 because I only as you have told it to run Latin one.
Now the question is why? Why is p-year faster than hotspot? Let's have a look.
So once again, I've done this a little bit of head-of-time, so I don't have to repeat it here all the things.
But we can move the profiling data from p-year.
This is going to take a little bit more exercise.
But what we can, if we go to the very top, if I can, or we can see it here.
The hottest method is the game-h-generated code.
But then something is really different, it starts to happen.
It gets to here. That's where things start to run.
And it starts one time, two times, three times, four times.
So what you see here is something the p-year does that hotspot can do today.
P-year can unroll a loop that is not counted.
A loop that is checks a bulletin value of whether it's done. It can unroll it.
Something that, from what I'm talking to my engineer fellow team members,
I've been opening the gate in, the gate kind of does that hotspot yet.
But still, p-year still keeps most of the original features, for example.
This line here that you see there, that's essentially extracting the code of field out of a string
and checking if it's Latino.
How do I know the C field of a string is the coder?
Well, I can look at, I can just pause to look at the string structure.
And we see the coder is in field 12, so I'm field C.
So this thing here is basically reading the coder, okay?
This field number four, that's the byte array.
Then this field four, we put it into array, and obviously, we had the before I go to the byte array,
the coder goes into EBP, test BPL, does basically change in whether the coder is Latino or not.
Then array R28 is basically then here, where are you going?
That's the byte array, the rates.
And then here is where we then eventually extract the chart out of that.
If R13 is this one, so we extracted the array,
basically we're taking the byte array value out of the string,
putting it into R13, and here in the R13, we basically extract the index, RBP index.
So we see that the structure of the coder is still pretty much the same.
Still, the performance is slightly better.
This is where we are at this stage of this investigation.
Obviously, we're going to do more investigation on this to understand how the,
these are rolling works, so if they are rolling, it's the reason why the performance increase.
Yep.
Still, it means that the ground has optimized the benchmark,
but not the coder, not the coder, not the coder.
Well, this is the implementation of the character.
Yeah, but the message to optimize this, the JMH generated message.
Yeah.
And it has optimized a loop in that message.
So that the coder's password, the character, not the coder.
Yeah.
And that's where you can add the compiler control annotations to tell you to never in line.
And that's what I mean, the process of adding, I was starting in as we are no way.
See, the reason why the JMH does the predicted is because we know that the compiler's regime is starting
Yeah.
But it is something that is interesting to know.
It's something that we need to understand why they are rolling,
but be something that makes things faster or not.
Still, it's something valuable.
Lessons to learn.
I feel out of that.
Yeah.
Obviously, things that are still in progress.
This is basically, as you can see, this is the first time we're speaking about this.
So, this is still working progress, but we learn in things.
So, that's all I really had today.
There's my slides, I've got things that I went through today,
a longer and some details on the assembly as well, more details.
And I want to leave you with this slide, where you can see this way.
We're just saying, I'm finishing a couple of links.
The first one is that's the report where I've been where you can find the JMH extension
to do what I've been doing today.
We don't get half a release of it.
We're trying to figure out the license has been already agreed.
We're using the single license at JMH, because obviously it's heavily rely on the JMH,
which is GPL2 with crosspath.
But we haven't done a Maven release, for example.
But you can still check it out.
You can build it and there are instructions on how to use it.
So, that's on this link.
The other link is basically what I've done today.
So, you can go through it on your own time.
And with that, that's all I wanted to say.
Questions, yeah?
It's like how you launch the Maven release.
But still, it's a Java Maven task.
So, this is the magic of the JMH extension, or...
Well, the thing is...
The thing is, you need to understand,
to understand what's going on to the JMH work.
JMH work is not a single Java process.
Normally, JMH has got a Java process,
but then launches the benchmarks when you do it for it.
There's no reason for that first benchmark to be native.
What we've done is what we are launching is native.
What do you mean is that you get the same experience
as you get normally with the Java version of JMH,
which is like launching a jar.
But underneath, instead of launching a Java process,
what I'm launching is a native process.
That's the method in your JMH.
Yeah, yeah.
It's a part of the poem.xml.
Basically, I make it show that basically the same thing works.
Yeah.
More questions?
Thank you.
Thanks for attending.