{"action":"created","issue":{"url":"https://api.github.com/repos/typemytype/drawbot/issues/185","repository_url":"https://api.github.com/repos/typemytype/drawbot","labels_url":"https://api.github.com/repos/typemytype/drawbot/issues/185/labels{/name}","comments_url":"https://api.github.com/repos/typemytype/drawbot/issues/185/comments","events_url":"https://api.github.com/repos/typemytype/drawbot/issues/185/events","html_url":"https://github.com/typemytype/drawbot/pull/185","id":295963959,"number":185,"title":"Adds a memoize decorator to cache frequently used getter functions","user":{"login":"andyclymer","id":2575594,"avatar_url":"https://avatars0.githubusercontent.com/u/2575594?v=4","gravatar_id":"","url":"https://api.github.com/users/andyclymer","html_url":"https://github.com/andyclymer","followers_url":"https://api.github.com/users/andyclymer/followers","following_url":"https://api.github.com/users/andyclymer/following{/other_user}","gists_url":"https://api.github.com/users/andyclymer/gists{/gist_id}","starred_url":"https://api.github.com/users/andyclymer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andyclymer/subscriptions","organizations_url":"https://api.github.com/users/andyclymer/orgs","repos_url":"https://api.github.com/users/andyclymer/repos","events_url":"https://api.github.com/users/andyclymer/events{/privacy}","received_events_url":"https://api.github.com/users/andyclymer/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":2,"created_at":"2018-02-09T18:19:51Z","updated_at":"2018-02-10T23:28:34Z","closed_at":null,"author_association":"CONTRIBUTOR","pull_request":{"url":"https://api.github.com/repos/typemytype/drawbot/pulls/185","html_url":"https://github.com/typemytype/drawbot/pull/185","diff_url":"https://github.com/typemytype/drawbot/pull/185.diff","patch_url":"https://github.com/typemytype/drawbot/pull/185.patch"},"body":"I noticed that the last commit in \"master\" slowed down some larger proofs of mine, I think the problem is just that it needs to repeatedly call some helper functions in openType.py every time you append to a FormattedString.\r\n\r\nWould you consider a fix like you find in this commit, that uses a memoize decorator to cache the results? It's one that I had found somewhere in the past and have been using in my own code.\r\n\r\nI think this is safe to cache the results from these \"get\" functions since the font isn't going to change while the script is running.\r\n\r\nExample script:\r\n\r\n```python\r\nimport drawBot as db\r\nimport time\r\n\r\nfs = db.FormattedString()\r\nruns = 100\r\ntotalTime = 0\r\nfor i in range(runs):\r\n    now = time.time()\r\n    fs.append(\"Test\", font=\"Operator-Medium\", openTypeFeatures=dict(smcp=True, liga=True))\r\n    totalTime += (time.time() - now)\r\nprint totalTime/runs\r\n```\r\nWith this script, the timer reports:\r\n``0.0019614815712`` seconds with commit d924098 (before OT feature changes)\r\n``0.0038596010208`` seconds with fd27983 (latest master)\r\n``0.0001614809036`` with caching in openType.py and variations.py\r\n\r\nThe difference might seem trivial, but a very very large proof now goes from taking 154 seconds to draw, down to 44 seconds. Nice!\r\n"},"comment":{"url":"https://api.github.com/repos/typemytype/drawbot/issues/comments/364704634","html_url":"https://github.com/typemytype/drawbot/pull/185#issuecomment-364704634","issue_url":"https://api.github.com/repos/typemytype/drawbot/issues/185","id":364704634,"user":{"login":"typemytype","id":1190358,"avatar_url":"https://avatars1.githubusercontent.com/u/1190358?v=4","gravatar_id":"","url":"https://api.github.com/users/typemytype","html_url":"https://github.com/typemytype","followers_url":"https://api.github.com/users/typemytype/followers","following_url":"https://api.github.com/users/typemytype/following{/other_user}","gists_url":"https://api.github.com/users/typemytype/gists{/gist_id}","starred_url":"https://api.github.com/users/typemytype/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/typemytype/subscriptions","organizations_url":"https://api.github.com/users/typemytype/orgs","repos_url":"https://api.github.com/users/typemytype/repos","events_url":"https://api.github.com/users/typemytype/events{/privacy}","received_events_url":"https://api.github.com/users/typemytype/received_events","type":"User","site_admin":false},"created_at":"2018-02-10T23:28:34Z","updated_at":"2018-02-10T23:28:34Z","author_association":"OWNER","body":"this could work\r\n \r\n`clearMemoizeCache()` could be called whenever a reset of a drawingTool is called.\r\n\r\nthis could work out of the box for `getFeatureTagsForFontName` as fontName is a simple string.\r\n\r\n```python\r\n\r\n_memoizeCache = dict()\r\n\r\ndef clearMemoizeCache():    \r\n    _memoizeCache.clear()\r\n\r\n\r\ndef memoize(function):\r\n    \"\"\"\r\n    Memoize a function's return value with the function's arguments.\r\n    The next time a function is called with the same arguments, the cache is returned. \r\n    Example usage:\r\n        @memoize\r\n        def addNumbers(first, second):\r\n            return first + second\r\n        # The first time this function is called the calculation will be made,\r\n        # and and the result will be stored in the cache dict as [first, second]: returnValue\r\n        # From then on, this value will be returned when the same argument is made to the addNumbers function\r\n    \"\"\"\r\n    def wrapper(*args):\r\n        if function not in _memoizeCache:\r\n            _memoizeCache[function] = dict()            \r\n        saved = _memoizeCache[function]\r\n        isHashable = True\r\n        for arg in args:\r\n            try:\r\n                hash(arg)\r\n            except TypeError:\r\n                isHashable = False\r\n                break     \r\n        if isHashable and args in saved:\r\n            print(\"cached\")\r\n            return saved[args]\r\n        else:\r\n            print(\"---- called ----\")\r\n            rv = function(*args)\r\n            if isHashable:\r\n                saved[args] = rv\r\n            return rv\r\n    return wrapper\r\n    \r\n\r\n@memoize\r\ndef myFunction(test, o=None):\r\n    return test * 3\r\n\r\n\r\nprint(myFunction(10, dict()))\r\nprint(myFunction(10, dict()))\r\nprint(myFunction(10))\r\nprint(myFunction(10))\r\nprint(myFunction(\"a\"))\r\nprint(myFunction(\"a\"))\r\nclearMemoizeCache()\r\nprint(myFunction(10))\r\nprint(myFunction(10))\r\nprint(myFunction(10))\r\n```"}}
{"actor":{"display_login":"typemytype"}}