3b1b / manim

Animation engine for explanatory math videos
MIT License
68.01k stars 6.07k forks source link

Performance issue #893

Closed qo4on closed 4 years ago

qo4on commented 4 years ago

I made a simple test where I draw multiple identical vertical lines from bottom to top in cycle. I found out that each new line renders much slower than previous one. Is it a bug or did I do something wrong?

def c(coord):
  if isinstance(coord, (np.ndarray, list)) and len(coord) == 3:
    return np.asarray(coord, dtype=np.float32) / 135
  else:
    return coord / 1.35

import numpy as np
from datetime import datetime

class mainScene(Scene):
  CONFIG = {
      "camera_config": {"background_color": '#ffffff'},
  }
  def construct(self):
    for j in np.arange(-958.5, 960, 2):
      start = datetime.now()
      for _ in np.arange(-540, 540, 2):
        line = Line(c([j, _, 0]), c([j, _+1, 0]), stroke_width=c(1), stroke_color='#000000')
        self.add(line)
        if _ == 538: print(int(j + 959.5), datetime.now() - start)

    self.wait(.1)

1 0:00:00.389817
3 0:00:00.927884
5 0:00:01.458466
7 0:00:01.970841
9 0:00:02.500315
11 0:00:03.015520
13 0:00:03.712525
15 0:00:04.210232
17 0:00:04.822479
19 0:00:05.358658
21 0:00:06.160043
zavden commented 4 years ago

I have no idea what you are trying to do, but if you use "datetime", the time you are capturing is the processing time, that is, the time it takes for your CPU to do the calculations. To use the animation time use self.time, like this:

class mainScene(Scene):
    CONFIG = {
        "camera_config": {"background_color": '#ffffff'},
    }
    def construct(self):
        for j in np.arange(-958.5, 960, 2):
            start = self.time
            for _ in np.arange(-540, 540, 2):
                line = Line(c([j, _, 0]), c([j, _+1, 0]), stroke_width=c(1), stroke_color='#000000')
                self.add(line)
                if _ == 538: print(int(j + 959.5), self.time - start)

            self.wait(.1)
qo4on commented 4 years ago

Your code prints this:

3 0.0
5 0.0
7 0.0
9 0.0

I don't care about animation time. My question is about performance: why does each new self.add(line) take more time than previous one? How can I avoid that? How can I keep performance stable regardless of the number of drawn objects?

For example, when I have 100 000 consequtive self.add(...) code lines (or self.play(...), it doesn't matter) which I'd like to draw in a predefined time schedule what should I do? After a couple of thouthands rendered objects the time that each new object needs becomes too large.

zavden commented 4 years ago

That is quite obvious if you think about how Manim should be working internally. Manim draws each object in each frame according to the preset order in the variable self.mobjects, as you already know self.add (mob) adds mob to self.mobjects. That means that the more mobjects added to the animation, more mobjects each frame will have to draw, for memory consumption. If you add 1000 mobjects with self.add then in each frame the 1000 mobjects will have to be added according to the order ofself.mobjects. There is no way around this because Manim was designed this way.

This behavior is not similar to self.play, because the animated object is not successively added to memory. For example:

self.add(circle)
self.play(circle.shift,LEFT)

In this animation there is only one circle object, and in the course of the animation no other mobject is added or removed, all objects in self.mobjects remain intact, self.mobjects is the same before and after the animation. There are some animations that can delete (FadeOut, etc.) or add (FadeIn, Write, GrowFromCenter, etc.) an object at the end, but they do not do it in large quantities as you intend to do so.

Manim was not designed for such complex animations, Grant himself on his official website has said this, if Manim is not trained to do this I recommend using some other animation tool, such as Blender (in which you can also use Python 3).

qo4on commented 4 years ago

Thanks a lot! As for my code example: I self.add 540 lines, each line is 2 pixels long. If instead I self.add 1 line 1080 pixels long what the perfomance benefit will be? Will it be 540 times faster or there is something else?

zavden commented 4 years ago

I don't know that, you could try to add all the lines first in a VGroup and then add that VGroup with self.add. Surely there are libraries that help you measure the performance of your code. But again, Manim was not designed for complex animations, you must use the right tool for the right job.

qo4on commented 4 years ago

Ok, thank you. What is the best way to remove objects completely so that they can not slow down scene computation?

zavden commented 4 years ago

self.remove(mob1,mob2,...)