-1

I built an algorithm (sieve of Eratosthenes) for finding primes, but it consumes a lot of memory. Currently, my code uses a decorator to monitor the time eclipsed. Could you come up with a similar decorator to evaluate the memory consumed by my program?

    import math
    import time


    def time_usage(func):
        def wrapper(*args, **kwargs):
            beg_ts = time.time()
            result = func(*args, **kwargs)
            end_ts = time.time()
            print("[INFO] elapsed time: %f" % (end_ts - beg_ts))
            return result
        return wrapper

    @time_usage
    def find_prime(n):
        is_composite = [False for _ in range(n + 1)]
        # Cross out multiples of 2
        for i in range(4, n, 2):
            is_composite[i] = True
        # Cross out multiples of primes found so far
        next_prime = 3
        stop_at = math.sqrt(n)
        while next_prime < stop_at:
            # Cross out multiples of this prime
            for i in range(next_prime * 2, n, next_prime):
                is_composite[i] = True
            # Move the next prime, skipping the even number
            next_prime += 2
            while next_prime <= n and is_composite[next_prime]:
                next_prime += 2
        # Copy the primes into a list
        primes = []
        for i in range(2, n):
            if not is_composite[i]:
                primes.append(i)

        return primes


    if __name__ == '__main__':
        print(find_prime(100000))

One suggestion is to use a third party library to profile the memory usage. I used the memory_profiler as it offers a nice decorator implementation however I cannot use both time_usage decorator and the memory profile.

Here I can see that @profile is actually profiling the memory of time_usage.

import math
import time
from memory_profiler import profile


def time_usage(func):
    def wrapper(*args, **kwargs):
        beg_ts = time.time()
        result = func(*args, **kwargs)
        end_ts = time.time()
        print("[INFO] elapsed time: %f" % (end_ts - beg_ts))
        return result
    return wrapper

@profile
@time_usage
def find_prime(n):
    is_composite = [False for _ in range(n + 1)]
    # Cross out multiples of 2
    for i in range(4, n, 2):
        is_composite[i] = True
    # Cross out multiples of primes found so far
    next_prime = 3
    stop_at = math.sqrt(n)
    while next_prime < stop_at:
        # Cross out multiples of this prime
        for i in range(next_prime * 2, n, next_prime):
            is_composite[i] = True
        # Move the next prime, skipping the even number
        next_prime += 2
        while next_prime <= n and is_composite[next_prime]:
            next_prime += 2
    # Copy the primes into a list
    primes = []
    for i in range(2, n):
        if not is_composite[i]:
            primes.append(i)

    return primes


if __name__ == '__main__':
    print(find_prime(100000))

Produce this :

Line # Mem usage Increment Line Contents

 7     27.4 MiB      0.0 MiB       def wrapper(*args, **kwargs):
 8     27.4 MiB      0.0 MiB           beg_ts = time.time()
 9     28.3 MiB      0.9 MiB           result = func(*args, **kwargs)
10     28.3 MiB      0.0 MiB           end_ts = time.time()
11     28.3 MiB      0.0 MiB           print("[INFO] elapsed time: %f" % (end_ts - beg_ts))
12     28.3 MiB      0.0 MiB           return result

[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, ..., 99989, 99991]

Michael
  • 1,950
  • 1
  • 29
  • 46
  • 1
    Writing your own prime sieve is a good exercise, but you may be interested in looking at the code at [Fastest way to list all primes below N](http://stackoverflow.com/q/2068372/4014959). That page is fairly old, so a lot of the code there is for Python 2, but it's still worth studying, IMHO. The scripts by Robert William Hanks are very good, & quite easy to convert to Python 3. – PM 2Ring Dec 29 '16 at 12:38
  • Thanks, I will definitively investigate this option, however, in this question, I am interested in monitoring both memory and time in an easy way so I can benchmark various algorithms implementation and see the real impact of different run time. For example, this code has a `O(N log(log N))` but uses an unreasonable amount of memory when the numbers are too large. – Michael Dec 29 '16 at 12:47
  • 1
    Understood. My comment wasn't addressing your profiling questions, I just thought you might like to check out other prime sieving strategies. :) And on that note, to handle large numbers without consuming too much RAM, a segmented sieve can be useful, eg [this one I wrote a few years ago](http://stackoverflow.com/a/26440674/4014959). And to test primality of really large single numbers take a look at the [Miller-Rabin algorithm](http://math.stackexchange.com/a/1638487/207316). – PM 2Ring Dec 29 '16 at 13:15
  • Thanks for those links. Very interesting! – Michael Dec 29 '16 at 14:13

1 Answers1

-1

There are quite a few memory profilers for Python. This answer lists some of them.

You could create a decorator that checks memory usage before the function call, after the function call and displays the difference. As long as you're running in a single thread, you should get the result you want.

Community
  • 1
  • 1
zmbq
  • 35,452
  • 13
  • 80
  • 153
  • Ok I will investigate memory_profiler but I thought it would exist a simple method to display memory difference without involving any third party libraries. Your comment on single thread is however interesting to keep in mind. Thanks – Michael Dec 29 '16 at 11:17