Note that there are some explanatory texts on larger screens.

plurals
  1. POPython generator vs callback function
    primarykey
    data
    text
    <p>I have a class that solves an exact cover problem using a recursive, backtracking algorithm. Originally, I implemented the class with a callback function I passed to the object during initialization. This callback is invoked whenever a solution is found. In looking at someone else's implementation of the same problem, I saw that they were using yield statements to pass a solution out, in other words, their code was a python generator. I thought this was an interesting idea so I made a new version of my class to use yields. I then ran comparison tests between the two versions and, to my surprise, I found the generator version ran 5 times slower than the callback version. Note that, except for switching in a yield for a callback, the code is identical.</p> <p>What is going on here? I'm speculating that, because a generator needs to save state information before yielding and then restore that state when restarting at the next call, it is this save/restore that is what makes the generator version run so much slower. If this is the case, how much state information is the generator having to save and restore?</p> <p>Any ideas from the python experts?</p> <p>--Edited 7:40 PDT</p> <p>Here is the solver code which uses yield. Replace the first yield below with a call to the callback function and change the following loop with the second yield to just a recursive call to solve for the original version of this code.</p> <pre><code> def solve(self): for tp in self.pieces: if self.inuse[tp.name]: continue self.inuse[tp.name] = True while tp.next_orientation() is not None: if tp.insert_piece(): self.n_trials += 1 self.pieces_in += 1 self.free_cells -= tp.size if self.pieces_in == len(self.pieces) or self.free_cells == 0: self.solutions += 1 self.haveSolution = True yield True self.haveSolution = False else: self.table.next_base_square() for tf in self.solve(): yield tf tp.remove_piece() self.pieces_in -= 1 self.table.set_base_square(tp.base_square) self.free_cells += tp.size self.inuse[tp.name] = False tp.reset_orientation() </code></pre> <p>The mail loop which invokes the solver (after initialization, of course) is</p> <pre><code> start_time = time.time() for tf in s.solve(): printit(s) end_time = time.time() delta_time = end_time - start_time </code></pre> <p>In the callback version, the loop is gone with just a single call to solve.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload