Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this Fizz Buzz generator significantly faster than this Fizz Buzz Iterator class?

After learning about iterator class methods and generators, I tested the performance characteristics of simple Fizz Buzz solutions utilizing each idiom:

>>> from timeit import timeit
>>> timeit('tuple(fizzbuzz.FizzBuzzIterator(10))', 'import fizzbuzz')
13.281935930252075
>>> timeit('tuple(fizzbuzz.fizz_buzz_generator(10))', 'import fizzbuzz')
7.619534015655518

According to timeit the generator function is about 1¾ times faster than the iterator class.

My question again: Why is this Fizz Buzz generator significantly faster than this Fizz Buzz Iterator class?

Fizz Buzz iterator class

class FizzBuzzIterator:

    def __init__(self, low, high=None):
        if high is None:
            self.high = low
            self.current = 1
        else:
            self.high = high
            self.current = max(low, 1)

    def __iter__(self):
        return self

    def next(self):
        if self.current > self.high:
            raise StopIteration
        else:
            c = self.current
            self.current += 1
            if (c % 5 + c % 3) == 0:
                return 'FizzBuzz'
            elif c % 5 == 0:
                return 'Buzz'
            elif c % 3 == 0:
                return 'Fizz'
            else:
                return str(c)

Fizz Buzz generator function

def fizz_buzz_generator(low, high=None):
    if high is None:
        high = low
        cur = 1
    else:
        cur = max(low, 1)
    while cur <= high:
        c = cur
        cur += 1
        if (c % 5 + c % 3) == 0:
            yield 'FizzBuzz'
        elif c % 5 == 0:
            yield 'Buzz'
        elif c % 3 == 0:
            yield 'Fizz'
        else:
            yield str(c)
like image 482
Winny Avatar asked Feb 08 '14 17:02

Winny


People also ask

What is the FizzBuzz problem?

The FizzBuzz problem is a classic test given in coding interviews. The task is simple: Print integers one-to-N, but print “Fizz” if an integer is divisible by three, “Buzz” if an integer is divisible by five, and “FizzBuzz” if an integer is divisible by both three and five.

What is the best solution for FizzBuzz?

The most popular and well-known solution to this problem involves using conditional statements. For every number in n, we are going to need to check if the number is divisible by 4 or 3. If the number is divisible by three, it will print fizz, if the number is divisible by it will print buzz.

Who invented FizzBuzz?

FizzBuzz is a very simple programming task, used in software developer job interviews, to determine whether the job candidate can actually write code. It was invented by Imran Ghory, and popularized by Jeff Atwood. Here is a description of the task: Write a program that prints the numbers from 1 to 100.

How do you make a buzz fizz in Python?

Introduction to Fizzbuzz Program in PythonIf the number can be divided by 3, it will output Fizz instead of the number. If the number is divisible by 5, the result will display Buzz instead of the number. And if the given number is divisible by both 3 and 5, Fizz Buzz will be printed instead of the number.


1 Answers

Because apparently, generators are implemented more efficiently than iterators.

Your first solution has the following interesting characteristics:

  1. It uses objects.
  2. For an iteration over n numbers, 3 + n methods are called and 2 + 4·n attributes are accessed, which are both potentially slow operations.
  3. Exceptions are used for control flow.

The second solution does none of these, instead it yields which means that the language runtime performs the heavy lifting. As the runtime is usually implemented in C, this can be far more optimized than the very high-level code of your first solution.

Next, you should consider what you are actually benchmarking. For a good benchmark you should choose different input sizes n and watch how the two solutions compare at different scales.

  • For very small n, we expect the initalization cost to dominate. This is consistent with your results, as performing a function call is less expensive than creating an object.
  • For larger n, we expect the features of the algorithm to dominate. As the algorithms are exactly the same, the graphs should have the same shape. But per iteration, the first solution has a much higher cost (four attribute accesses, one method call). The two solutions would then be graphs with slightly different slopes. The exact relation of the per-iteration costs can only be assessed by obtaining a large data set of many timings for many input sizes n, and then fitting a function to that data.
like image 169
amon Avatar answered Oct 21 '22 01:10

amon