Hello, in this course I'll show you how to profile an application in Pharo.
That means measuring the execution of expressions
to see if we can replace them with others to gain time,
during the execution of a program.
Basically, what people say is, if you've never profiled
an application you could increase speeds by 40-50%.
I'm not convinced. I don't know, I've no proof.
But I can show you the tools in Pharo
for doing this profiling.
The first expression in Pharo is "timeToRun".
It's a message we send to a block,
which will give us the run time of an expression.
So, here in "1000 factorial",
I want to measure the run time of "1000 factorial", I put it in a block
and I send the message, "timeToRun".
Now that we have 'timeToRun" it's easy to compare 2 executions.
Here's a little exercise:
If I do "select" followed by "collect",
is it slower than "select:thenCollect",
knowing that this involves two passes
in the first case and one in the second.
So I think it will take longer, but we're going to check.
What do I do? I create my collection in both cases,
I'm looking, I create my collection with 10 elements, a small collection,
and what I'll do is create a certain number of times,
I'll make a loop so I can run the expressions several times.
You can see, in the first part,
I've my collection, I select every element larger than 5,
and afterwards, I'll square the elements I've selected,
and in the 2nd expression, I'll say, what do I want to select?
Everything larger than 5, and I'll square them.
We see that the system tells me that's 500 milliseconds,
and that's 300 milliseconds. The 2nd expression is better.
You see how it works, it's up to you to do it.
Note that we're sometimes obliged to put very large loops inside,
to amplify the measurement, for both cases, of course.
Otherwise, there's another operation, another message called "bench".
"Bench" will tell us how many times the code,
which is being studied, is executed over 5 seconds.
I take my "1000 factorial bench" example again, and I can execute it
610,000 times a second.
In this case, if we so desire, we can also make it
that the figure is as high as possible.
There's also a variant of bench, "benchFor",
where you can specify the size. What's interesting to see
is that "2 seconds", it's really the "second" message,
which is sent to message 2, which will give us a duration,
an instance of the Duration class,
and which will control how long the system runs.
This shows you once again that everything's an object in Pharo.
I set the seconds at 2 seconds. I send the "second" message to object 2.
What you have next, there's also what we call a "profiler",
which is sampling-based.
What will happen is, you'll execute your program,
and it will go at regular intervals
to inspect the execution stack,
see what's in the stack and collect information.
So how do we bring it up? We do "timeProfiler, spyOn"
and we'll pass it a block. I want to repeat 20 times
that I'll show on the Transcript,
"1000 factorial",
which I convert into a string, which usually costs the most.
After that, the profiler will give us this kind of result,
with a tree showing us the branches amongst which we've spent most time.
It shows us we spent 63% of the time in the method "printOn:base".
It's just to show you it's there.
There's a profiler you can play with if you're interested.
So to summarize,
to begin with,
you can use "timeToRun", "bench" and the profiler.
There's a chapter in the book "Deep into Pharo" showing this kind
of optimization technique.
I just wanted to point out, as you see in these 3 examples,
we always use blocks. This is a block...
This is a block too... Why?
You saw in the course on blocks that they allow you
to freeze the expression, the definition of the block
doesn't execute and give "bench" the possibility
of controlling the number of executions
or deciding when execution of the program will begin.
The other thing I'd say is,
find out about how "timeToRun" and "bench" are implemented.
To do that you click on Expression
and do "browse" or "implement" and you'll see the code.