Lecture thumbnail 0:00 / 0:00 All right.

So we’re now going to take a look at the flyweight pattern, which is all about space optimization.

And we’re going to work with strings, surprisingly enough.

Now, remember I mentioned that string interning in dot net is one example of a flyweight, because

if you make identical strings, then those strings are going to be referring to the same area in memory.

And since in dot, net strings are immutable, this is a very convenient move by dot net to kind of

prevent additional allocations.

But we’re going to set up a scenario where strings are not going to be interned and we’re going to manipulate

them ourselves.

So here’s the idea.

Let’s suppose we have a multi-user system and we have a user in that system.

Now let’s suppose that when the user signs up, they have the full name that they provide.

So we’re going to have string full name.

So that’s going to be a field and then we’ll have a constructor for initializing the full name as soon

as F begins to work.

Or maybe, maybe it has stopped working.

There we go.

Finally, after so many attempts.

So we have this class and we can imagine this situation where you have hundreds of thousands of users

with similar names, not necessarily identical, but they might have similar first names or last names

or whatever, and we might want to optimize the memory usage.

Now in order to actually test memory usage, I’m going to be using something new, something we haven’t

used before.

What I’m going to be using is a framework called memory unit.

And let me just get this from a nugget package.

So Dot Memory Unit is a unit testing framework specifically for memory for memory consumption.

And that’s what we’re going to look at.

We’re going to look at memory consumption.

So we’re going to choose Memory Unit here.

It is a free framework provided by Jetbrains.

So I’m going to go ahead and install this nugget package and then we’ll have fun actually using it and

looking at how much memory a particular set of objects takes up, because that’s what we’re interested

in.

We’re interested in finding out how we can optimize the space taken up by strings in our case, because

that’s what it’s all about.

It’s string allocations.

Okay?

So what I’m going to do is I’m going to set up a unit testing scenario where the number of objects being

allocated is rather ridiculous.

So I’m going to simulate a real life kind of high load system.

So we’re going to have a class called demo.

I’ll put a main in here so that it compiles, but we’ll have it as a test fixture.

So that’s going to be N unit.

And in addition, I will have a test and we’ll just have a test called test user.

All right, So test user.

Now the idea is that I’m going to generate a rather large number of first and last names, and then

I will create a list of users and we’ll see how much memory the actual users take up.

But we’re interested in the overall memory consumption generally.

So not just the users, but also the strings which the users employ.

So first of all, let’s generate those strings.

I’m going to generate a bunch of first names, names, and the way I’m going to do it is I’ll just make

So I’ll say enumerable dot range.

So I’ll take 0 to 100.

So I’m going to have 100 elements and I will select them using an algorithm called random string.

There we go.

Okay, so random string, which I’m just going to define up above as a method, and that’s going to

be a string returning method, by the way.

So let’s get a string in here.

So this is just going to generate a random string.

So I’ll say random rand equals new, random like so.

And then what I’m going to do is I’m going to say, let’s generate a bunch of characters.

So I’m going to return a new string.

Return new string, which consists out of, let’s say, ten characters.

So we’re going to do enumerable dot range to generate ten characters.

And then I will select so I will select for each character.

So for each index I will select a character which is essentially the letter A plus a random value from

0 to 25.

So that’s going to be around next 26 like so.

And then I will get this range and I will turn it to array and that way you can feed it into a string

constructor.

So here’s my random string.

I’ve generated a 100 random first names and I will generate a 100 random last names as well.

Last names like so.

Okay, so we’ll make a list of users.

Var users equals new list of user user like so user.

There we go.

Okay, so we make a list of users and then what we do is we go through each of the first names and each

of the last names and we make a user.

So we’re going to have 100 by 100.

We’re going to have 10,000 users, which should hopefully give us an indication of whether we’re getting

any memory benefits.

So I’m going to say for each Var first name in, first names for each var last name in, last names,

and then I’m going to say users dot add new user.

And then let’s interpolate the first and last name.

There we go.

Okay, so we’re interpolating the first and last name.

Now, what I want to do just for the fun of it is I’m going to force garbage collection.

So if we have any junk, like the temporary strings that are used in the constructor here, they are

actually garbage collected.

So I’m going to force GC.

It’s going to be that’s another method.

So let’s put this in here for example.

So I’m going to do GC collect, wait for Finalizers and then GC collect again.

So GC dot collect and then gc dot wait for pending Finalizers.

There we go.

Okay, so we force GC and now we want to see how much memory we took and I’m just going to write out

the overall memory.

So I’m going to say dot memory, dot memory dot.

It should be like this.

So we get the import memory check and then here is the lambda.

So we pass in the memory and then we write line the total amount of memory that was occupied.

So we’re going to see memory dot size in bytes.

There we go.

So this is our test.

And in order to execute this memory unit test, you don’t just go alt-enter and run.

Instead, you choose run under the memory unit.

So I forgot a semicolon here, but once I got that in, let’s actually execute this and let’s see how

much memory we take up.

All right, so we got a value.

We have this value.

I’m going to copy it and paste it right next to our test so we remember what it is.

So this is the size in bytes that all the memory in this particular test occupies.

So it’s not particularly scientific, but we have this indicator of 1,655,033 bytes being occupied.

So now what we can do is we can try to optimize this very simple situation with the full name by splitting

the full name into first and last name and then simply storing the references to those objects.

I’m going to make a new class.

It’s going to be a class called User two and it’s going to have the same constructor.

So it will take a full name like so, but we’re going to store things differently.

So we’re going to store a static cache of all the possible strings.

So I’m going to have a static list of string, let’s call it strings.

Let’s just initialize it with an empty list.

And here what I’m going to do is a bit more complicated, shall we say, because I want to keep all

of the names.

So the first and last names, I want to keep them in this list.

So how can I do it?

Well, first of all, I’ll make an inner function called get or Add.

So this is going to be an inner function which takes a string.

And here I will try to get the index of this element in strings.

So I’ll say int index equals strings dot index of I’ll try to find the string in the elements that we

have already cached and if we already have that index.

So if index is not equal to negative one, then we can return the index because we already have the

string cached.

Otherwise what we do is we add it to the set of items.

So we say Strings dot add and we add the full name or rather the string that gets passed in, not the

full name, the string that gets passed into our inner function.

And then we return the its index, which is strings dot count minus one.

That’s the index of the last element.

So this is an inner function and then we’re going to call it on every single element from the full name.

So we’re going to split the full name, We’re going to call, get or add on every part of the full name,

and we’re going to store all the indices in a private array.

And this is going to be a private integer array, integer array called names.

For example, there we go.

Okay.

So what I’m going to do here is I’m going to say that names this new array that I’ve just made names

is equal to.

So we take the full name, we split it, split on a space, and then we call get or add on every single

element.

So we select, get or add, and then we turn it to an array.

There we go.

So we’re only storing the indices and all the strings that get stored in the list of strings.

So if you want to assemble the full name, for example, if you had to assemble the full name back,

all you have to do is write something like public string full name, and that would be a property with

a getter which does string dot join with a space on the actual names.

So you grab the names which are indices and then you you transform them.

So in this case, it would have to be something like names, dot select names, dot select, and you

would have the index and you would put the index into strings.

So you say strings at this particular index and you would and that’s pretty much it.

That’s all that you have to do, except that now if we call this, we get a bunch of strings and so

we do string dot join with a space, we’re getting a bunch of strings, let’s turn them into an array.

Let’s see what’s actually going on here.

So we’re trying to join char and string array, but it only does string and string array.

Okay?

So that can be a space like this.

So it can be a string space.

There we go.

And now I don’t think we need to array anymore.

Okay, so this is the setup.

I’m just showing you full names so that you have an idea for how it’s implemented.

And now we can write an identical test.

So we’ve built this type user too, and we can try building an identical test.

So I’m just going to duplicate this particular test control D here and I will say test user.

To let’s put a list of user to here.

There’s going to be a user to here, but apart from that, everything is the same.

So now what we can do is we can build and then we can actually run this test using memory unit.

Hooray.

And here is the other value.

So let me copy that and paste it well, paste it next to this one.

So as you can see, the second approach has saved us, what is it, about 300kB worth of memory.

Now, I know this isn’t a particularly scientific test, but it makes perfect sense because we’re storing

a lot fewer strings.

There is a lot less diversity in strings.

So instead of storing 100 by 100, which is 10,000 strings, we’re storing just 100 plus 100, which

is 200 strings.

So that’s where we get the memory saving.

As you can see, the memory saving isn’t it’s not dramatic and it’s unlikely to show up unless you have

lots and lots and lots of users.

However, if we’re talking about not an in-memory store but a database, this approach makes even more

sense that way.

And so this is how you implement the flyweight pattern.

Play Stop Play Start Play information alert