├── .gitignore ├── .ipynb_checkpoints └── Untitled-checkpoint.ipynb ├── README.md ├── Untitled.ipynb ├── license.txt ├── pymemory ├── __init__.py └── pymemory.py ├── setup.py └── tests ├── __init__.py └── test.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_Store 3 | pymemory.egg-info/ -------------------------------------------------------------------------------- /.ipynb_checkpoints/Untitled-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [], 3 | "metadata": {}, 4 | "nbformat": 4, 5 | "nbformat_minor": 2 6 | } 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pymemory 2 | inspect memory footprint of python objects. 3 | 4 | ## install 5 | ``` 6 | git clone github.com/mynameisvinn/pymemory 7 | cd pymemory 8 | python setup.py install 9 | ``` 10 | 11 | ## example 12 | for example, we'd like to determine size of int `1`. since a python object consumes 16 bytes of overhead and each int is 1 byte, so we should expect 24 bytes for `1`. 13 | ```python 14 | >>> from pymemory import deep_getsizeof 15 | >>> x = 1 16 | >>> deep_getsizeof(x, set()) # prints 24 bytes 17 | ``` 18 | 19 | ## a deeper dive into python internals 20 | in the above example, a cython struct `PyLongObject` was created to hold the integer 1. 21 | 22 | a PyLongObject struct holds three attributes: a `PyObject_HEAD` object (16 bytes); length of array (4 bytes); and an array for value (another 4 bytes). 23 | 24 | the `PyObject_HEAD` is a struct: 25 | ```c 26 | // https://code.woboq.org/llvm/include/python2.7/object.h.html 27 | 28 | /* PyObject_HEAD defines the initial segment of every PyObject. */ 29 | PyObject_HEAD 30 | { 31 | size_t refcnt; // 8 bytes 32 | typeobject *type; // 8 bytes 33 | } 34 | ``` -------------------------------------------------------------------------------- /Untitled.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": true 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "from pymemory import deep_getsizeof" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 2, 17 | "metadata": { 18 | "collapsed": true 19 | }, 20 | "outputs": [], 21 | "source": [ 22 | "x = 1" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 3, 28 | "metadata": {}, 29 | "outputs": [ 30 | { 31 | "data": { 32 | "text/plain": [ 33 | "24" 34 | ] 35 | }, 36 | "execution_count": 3, 37 | "metadata": {}, 38 | "output_type": "execute_result" 39 | } 40 | ], 41 | "source": [ 42 | "deep_getsizeof(x, set()) # prints 40 bytes" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": { 49 | "collapsed": true 50 | }, 51 | "outputs": [], 52 | "source": [] 53 | } 54 | ], 55 | "metadata": { 56 | "kernelspec": { 57 | "display_name": "Python 2", 58 | "language": "python", 59 | "name": "python2" 60 | }, 61 | "language_info": { 62 | "codemirror_mode": { 63 | "name": "ipython", 64 | "version": 2 65 | }, 66 | "file_extension": ".py", 67 | "mimetype": "text/x-python", 68 | "name": "python", 69 | "nbconvert_exporter": "python", 70 | "pygments_lexer": "ipython2", 71 | "version": "2.7.13" 72 | } 73 | }, 74 | "nbformat": 4, 75 | "nbformat_minor": 2 76 | } 77 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 vin tang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pymemory/__init__.py: -------------------------------------------------------------------------------- 1 | from pymemory import * -------------------------------------------------------------------------------- /pymemory/pymemory.py: -------------------------------------------------------------------------------- 1 | """This module finds memory footprint of a python object.""" 2 | 3 | from sys import getsizeof 4 | from collections import Mapping, Container 5 | 6 | def deep_getsizeof(obj, ids): 7 | """Find the memory footprint of a Python object 8 | 9 | This is a recursive function that drills down a Python object graph 10 | like a dictionary holding nested dictionaries with lists of lists 11 | and tuples and sets. 12 | 13 | The sys.getsizeof function does a shallow size of only. It counts each 14 | object inside a container as pointer only regardless of how big it 15 | really is. 16 | 17 | Parameters 18 | ---------- 19 | obj : object 20 | ids: 21 | 22 | Return 23 | ------ 24 | obj_size : int 25 | return the size of an object in bytes. 26 | """ 27 | if id(obj) in ids: 28 | return 0 29 | 30 | obj_size = getsizeof(obj) 31 | ids.add(id(obj)) 32 | 33 | if isinstance(obj, str) or isinstance(0, unicode): 34 | return obj_size 35 | 36 | if isinstance(obj, Mapping): 37 | return obj_size + sum(deep_getsizeof(k, ids) + 38 | deep_getsizeof(v, ids) for k, v in obj.iteritems()) 39 | 40 | if isinstance(obj, Container): 41 | return obj_size + sum(deep_getsizeof(x, ids) for x in obj) 42 | 43 | return obj_size 44 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name="pymemory", 5 | version="0.1.0", 6 | py_modules=["pymemory"], 7 | test_suite='tests' 8 | ) 9 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mynameisvinn/pymemory/6b1f79489a0d8a3947f81ebaa6a082ec39769325/tests/__init__.py -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | """ 2 | pymemory tests 3 | """ 4 | import unittest 5 | from pymemory import deep_getsizeof 6 | 7 | class InitializationTests(unittest.TestCase): 8 | """ 9 | tests 10 | """ 11 | 12 | def setup(self): 13 | pass 14 | 15 | def test_integer(self): 16 | x = 5 17 | assert deep_getsizeof(x, set()) == 24 18 | 19 | def test_float(self): 20 | x = 5.3 21 | assert deep_getsizeof(x, set()) == 24 22 | 23 | def test_emptystring(self): 24 | x = "" 25 | assert deep_getsizeof(x, set()) == 37 26 | 27 | def test_string(self): 28 | x = "1234567" 29 | assert deep_getsizeof(x, set()) == 44 30 | 31 | def test_emptyunicode(self): 32 | x = u"" 33 | assert deep_getsizeof(x, set()) == 50 34 | 35 | def test_unicodestring(self): 36 | x = u"1" 37 | assert deep_getsizeof(x, set()) == 104 38 | 39 | def test_emptylist(self): 40 | x = [] 41 | assert deep_getsizeof(x, set()) == 72 42 | 43 | def main(): 44 | unittest.main() 45 | 46 | if __name__ == '__main__': 47 | main() --------------------------------------------------------------------------------