When I wrote mine, something I ran into was realizing that often the CPU and PPU are just doing the same thing over and over and over. If only I could cache all that work.
For example, you don’t modify the previous frame. You have to draw the entire frame every time, writing sprites from OAM memory every time. Sometimes games do clever stuff here for effects, but a lot of the time you’re just drawing the same stuff. Like Tetris is almost completely a routine of drawing almost entirely the same graphics every time. Most of the cycles and OAM writes are just juggling sprites to draw the same layout every time.
I documented the heck out of the PPU because it gave me the second most grief (sound being the worst by far) if you are curious: https://github.com/ablakey/gameboy/blob/master/src/guest/sys... (goodness I’m proud of this project. Don’t tell my boss. He’ll want this level of documentation at work ;) )