Welcome back!
As I've promised in the last lecture, now, I'm going to show you how to add a buffer to optimize the program.
Let's get started!
When I call a Print function,
it needs to talk to the Operating System, and that is an expensive operation.
So, instead of doing it like so, I'm going to draw the board to a buffer first, and then I'm going
to print the buffer once on each loop.
So basically I will be drawing the board into the memory. After each loop step,
I will need to reuse the same buffer as well, or using a buffer would be meaningless.
It's because creating a buffer also takes time. Remember,
I need to draw images.
So I need a rune slice as a buffer.
I can do so using the make function because I know the exact capacity of my buffer. I've multiplied the
width by height because I want my buffer to include every cell in my buffer.
I'm only going to append to the buffer,
so that's why I have declared the single dimensional slice.
If I had declared a multidimensional slice, I would need to initialize each one of the inner slices.
However, in this case, I use a single dimensional slice.
So I can initialize it using the make function only once.
So why I use a multidimensional bool slice for the board? It's because, in that case, I'm going to access
the inner elements.
So using a multidimensional slice makes it easier to work with. Anyways.
So as you can see, in this case, a buffer is just a slice.
I'm going to reuse this slice after I draw it to the screen. Now I need to fill the buffer, instead of
calling the print functions. Let me first comment out the first print call here. Then, I'm going to add
the current cell value, and the space character into the buffer instead.
Here, I print a new line.
So instead of this, let me add a new line character into the buffer, like so. Okay now it's time to print
the buffer to the screen like so. Why does it print a lot of numbers?
The answer is that the buffer is a rune slice. Remember a rune is a numeric type.
So a rune slice is actually a bunch of numbers.
As I said, numbers can be converted to string values.
So now I'm going to convert the buffer into a string value like so.
Cool! It works.
You'll learn more about the numbers and strings later on.
No worries. OK, so what is the benefit of using a buffer?
Let me show you how to measure the performance of this program.
So you can better understand the benefits of using a buffer. However, measuring the current program won't
give us enough data because the current program doesn't do anything much.
We need to see the real performance optimization between a buffer and a non-buffered approach.
So what should I do?
Well I can exercise the current program
a lot of times to get a meaningful measurement using a loop like so. If you're using a bash command prompt
you can measure your program by using the time command.
If you're not using a bash prompt,
check out the link in the resources to find an alternative, if you're using Windows and not using a bash
prompt.
Let's assume that you're using a bash command prompt, so you just need to call "time".
Then as an argument you need to pass it the command that you want to execute.
In my case it is an ordinary "go run main.go".
Lastly, you need to redirect the output of the program to the null device.
Actually, this last step is not necessary.
If you don't redirect the output it is going to print the smiley face hundreds of times to the console.
Try it and you'll see what I mean. As you can see, the time program prints a few details about how much
time it takes to complete, and how much time it spends in the user mode, and in the kernel mode.
For now, I'm only interested in the total compilation time because that is what I'm trying to optimize
for, right. Now. let me change the program, so that it won't use a buffer, and instead it will print directly.
All right! Let's try it.
Interestingly, it looks like our buffered approach is much slower than a non-buffered approach.
However, I think we have made a mistake somewhere. In my experience using a buffer in this situation should
have made the program faster but it didn't.
So let's find out what's wrong with our buffered program.
Let me make the program use the buffer again.
I think I have found the problem here. Can you also see it?
Please pause the video and think about it.
OK, the problem is that the append calls allocate a lot of backing arrays unnecessarily, and this is the
reason behind the slowness of the program.
After each loop step, I need to use the same buffer again, or using a buffer here doesn't make a sense.
I can do so by reducing the length of the buffer to 0,
like so.
So the append calls will use the previous buffer, so they won't cause allocating new backing arrays. They
will start from the first element.
If you don't remember how the append and backing arrays work,
please watch the slice internals lectures.
As you can see, now the program is faster than the non-buffered version.
So, in summary, when you call the Print functions directly they need to talk to the operating system, and
doing so is expensive because the program spends a lot of its time talking to the Operating System, and
waiting for a response. However, if you forget reusing the same buffer, that is worse than not using
a buffer at all.
By the way, the loop here was for the measuring the execution time better.
I don't need it anymore, so I'm going to remove it. As you'll see in the next lecture that I'm gonna add
it back again.
But not for measuring the execution type,
I'll do it for animating the bouncing ball.
All right, that's all for now.
This is not the only way to measure the efficiency of this program but it's one of the easiest ways.
All right.
In the next lecture I'm going to animate the ball.
See you there.