# Copying Memory in EVM, how hard can that be?

- Speakers: [Elia Anzuoni](https://streameth.org/speakers/elia-anzuoni)
- Channel: [Devcon 7 SEA](https://streameth.org/devcon_7_sea)
- Date: 2024-11-13
- Duration: 08:16
- Watch: https://streameth.org/watch/67343c129dbb7a90e1a51151

## Description

Memory copy operations in EVM are a useful feature, but there are different ways to do. How do they differ? Which is the best?
The options are:
MLOAD+MSTORE loop
Identity Precompile
The new MCOPY opcode
Based on concrete examples we will explain how these options differ. We will use different examples as the amount of bytes copied makes a difference. For all these options we will present gas consumption and code size.
This way we can compare the different options to copy memory and crown the ult

## About the speakers

### Elia Anzuoni

Newcomer to Ethereum, maths-nerd from day 1.


## Transcript

Hi everyone, am I audible? Good to see you. I'm Elian Suoni, I also work at Chain Security. I can guarantee this was not on purpose. We didn't bribe the organizers. It was so nice of them. My topic for today is arguably the best fit for a lightning talk, because it turns out it's not that hard to copy in the VM, especially since Cancun. But yeah, we'll see exactly how much. So yeah, the right way to think about it, in my opinion, is before Cancun and after Cancun. Because since Cancun, we have a new opcode that does exactly that. And it's arguably the best way to do it. But before then, you had to implement this some other way. And there were two main ways to do that, to copy from memory to memory in the EVM. So one was to do an M load plus M store loop. So you would do 32 bytes at a time. You would load 32 bytes from memory onto the stack and store them back from the stack to the destination in memory. And this used to be, well, quite heavy in terms of code size because it's a full loop. And it also had quite a bad gas cost in terms of gas cost per memory word copied. The alternative was calling the identity recompile that we just saw with the last presentation, and while that has a little less code size because it's just a static call that you can do to the contract, still the gas per memory word is good enough. It's only three gas per memory word that you copy. But the overhead is quite big because you have to do a call. So it's at least, I think it's 180. Yeah, we'll see later. Anyway, since Cancun, all our problems are solved because we have a new opcode that does exactly that. It has the least code size because because it's just one off code, the least amount of gas. So now there's no reason not to use it. And as I'll tell you, it's very much widespread now. So let's just go quickly over some numbers. One way to implement the emload and store loop is in software. This is done in a quite popular, yet a bit old, Solidity library. And because it's done in high-level Solidity rather than entirely in assembly, then you see you have two checked additions, a checked subtraction, all this done for every memory word that you copy, which adds up to a gas cost of around 286 gas per memory word copied, which is quite bad. Then we have the same concept implemented entirely in assembly, which is what the Solidity compiler will auto-generate for you. It's a function. If you inspect the IR, it's called copy memory to memory with cleanup, I think. And yeah, it's a tighter loop, so it has less overhead. So overall, the cost per memory word copied is only 67, yeah, 67 gas. And the code size is also much smaller. Then just to see an example of how the identity recompile would be used, it's used by C4 library. And also it's what Vypr used to generate to copy memory to memory before Cancun. And yeah, the cost per word is the best. It's just three words, three gas. But the overhead, the constant term, as you can see, is quite big because you have to make a static call that has also a fixed cost of 15 inside, and then you have to do some checks. So it's not ideal for small copies. Now, keep in mind that we're neglecting the memory expansion cost, because that'll be the same whichever method you opt for. And also, we're doing this comparison right now with these opcode pricing, but keep in mind that those have changed and will change again in the future. So whatever conclusion on which method might be the most convenient now maybe wasn't the same. You wouldn't have come to the same conclusion five years ago, for example. But yeah, just to be sure about it, now the mcopy opcode is used by both Solidity and Viper for Cancun contracts, and the gas cost is the least, the code size is the least, so everything is good, the future is bright. I didn't have the time to add the slides to this, but from the same analysis that Dominic and the other colleagues at TuneSecurity did, it turns out that already now the mcopy opcode is already used more than the identity precompile, even though it's only quite new since Cancun. So it shows that, I mean, I don't know exactly how to phrase it. Maybe there's a lot of new contracts deployed that already outnumber the number of, I guess, Solidity Viber contracts for legacy. So, yeah, it's quite popular already. Then, yeah, I've prepared a remix setup for you. And also, if you want to shoot a message to our sales representatives to get an audit, which is never a bad idea, feel free to do so. Yeah, I'll take any questions. Thank you for the session. Please do you have questions? Oh, wow. You have a lot today. I'll try. Hello. Hello. You said that the memory expansion cost is the same in all three cases. Yeah. But my concern is that with the loop option, if the memory you are copying is not a multiple of 32, not only you may copy memory you don't need to copy, so that's more expansion, but also you may override something that is outside of your destination, like just after your destination. Yeah, so the memory expansion cost is actually I'm only 87% sure about this, but it's only computed on the number of memory words rounded up. So even if you end up writing at most 31 bytes, you don't incur memory expansion cost for another word. So it is the same, even if you overwrite within the same word that you've already paid for. Then, what was your other question? Ah, for overlapping, yeah. If in some of those functions are specified for cases where the overlap is okay, for example in the Yule one, because I think that one is only used to allocate at the tip of the memory so there's nothing afterwards. But in cases where that's not okay, for example the library, they do special tricks, like they round it up with an m store that's aligned to the last byte that you want to write to, and the rest, they mask it with the content that's already there. So you're basically writing back what's already there on the part that overlaps, and just on the new part, say the 20 bytes you'll write the new stuff. One more question for Elias. Okay no question. I'm curious Elias, so as a non-technical person, being almost everybody's a technical here, How do you communicate to a novice like me to be able to comprehend in simple language? Huh. I think I have a knack for it. It just comes natural to me. For me, I don't think I've understood something until I can explain it to my mom. So I guess... Thank you so much, guys. For me, I don't think I've understood something until I can explain it to my mom. So I guess that's it. Thank you so much, guys. Let's give him a hand of applause. Wow.
