{"next_page": 2, "previous_page": null, "max_next_page": 10, "posts": [{"oid": "claude-opus-is-10x-faster-than-openai-gpt-5-at-non-streaming-completions", "title": "Claude Opus is 10x faster than OpenAI GPT 5 at non-streaming completions", "pub_date": "2026-07-24T15:48:18.297Z", "comments": 0, "categories": ["Python", "AI"], "html": "<p>This picture summarizes it well:</p>\n<p><a href=\"/cache/72/7a/727a021ed23a566f90dcb07968a07b4c.png\"><img src=\"/cache/72/7a/727a021ed23a566f90dcb07968a07b4c.png\" class=\"fullsize\"></a></p>\n<p>Here on my blog, for <a href=\"/plog/blogitem-040601-1\">this popular blog post</a> I get a lot of comments. 28k blog comments over the years. Some of them are terribly written and hard to understand, so I let AI suggest a rewrite. That code that sends the blog post comment to AI, I actually fire off three times: once with OpenAI <code>gpt-5</code>, once with OpenAI <code>gpt-5-mini</code>, and once with Claude <code>claude-opus-4.8</code>. I use my human eyes and judgement to evaluate the results, and I can tell you they do equally well. Only the slightest differences.</p>\n<p>The surprising thing is how amazingly slow OpenAI's <code>gpt-5</code> is! It's nearly 10x slower than <code>claude-opus-4.8</code>. What's up with that!?</p>\n<p>It's also clear that the latency difference between <code>gpt-5-mini</code> and <code>gpt-5</code> is significant. At the time of writing, the input token price difference between <code>gpt-5.4</code> and <code>gpt-5.4-mini</code> is $2.50 compared to $0.75! That's a 3x difference.</p>\n<h3 id=\"conclusion\"><a class=\"toclink\" href=\"#conclusion\">Conclusion</a></h3>\n<ul>\n<li>\n<p>If you're constructing a prompt the API, use Claude.</p>\n</li>\n<li>\n<p>If you have to use OpenAI, consider the <code>mini</code> model because it's <em>both</em> cheaper <em>and</em> faster.</p>\n</li>\n</ul>\n<h3 id=\"bonus\"><a class=\"toclink\" href=\"#bonus\">Bonus</a></h3>\n<p>Before I added Claude, I used to use <code>litellm</code> to wrap OpenAI's models. The code looks like this:</p>\n<pre><code class=\"hljs\">\nresponse = litellm.completion(\n    model=<span class=\"hljs-string\">&quot;openai-gpt-5&quot;</span>,\n    api_key=settings.OPENAI_API_KEY,\n    messages=my_prompt_messages,\n)\n</code></pre>\n\n<p>Unlike, if you use the native OpenAI Python SDK the invocation looks like this:</p>\n<pre><code class=\"hljs\">\nclient = openai.OpenAI(api_key=settings.OPENAI_API_KEY)\nresponse = client.responses.create(\n    model=<span class=\"hljs-string\">&quot;gpt-5,\n    input=my_prompt_messages,\n)\n</span></code></pre>\n\n<p>I measured the difference, in speed, where I compare using the OpenAI SDK versus the <code>litellm</code> wrapper and the difference looks like this:</p>\n<p><a href=\"/cache/7b/c0/7bc0bdb8d1a10f3edf6d35a75c998541.png\"><img src=\"/cache/7b/c0/7bc0bdb8d1a10f3edf6d35a75c998541.png\" class=\"fullsize\"></a></p>\n<p>Granted, in June I \"only\" did a bit over 30 of these calls, but strangely there's a difference!<br />\nI don't have the intricate knowledge to understand why the <code>litellm</code> makes the total time different from using the SDK. (not sure I care either!)</p>\n<p>Either way, I'm moving away from <code>litellm</code> and only use the SDKs provided by Claude and OpenAI. Feels safer given the CVEs we've seen this year on <code>litellm</code>.</p>", "url": null, "disallow_comments": false, "split": null}, {"oid": "best-django-redis-configuration", "title": "Best Django Redis configuration", "pub_date": "2026-07-20T19:11:05.415Z", "comments": 0, "categories": [], "html": "<p>Latest <a href=\"/plog/best-django-redis-configuration-for-speed-and-size\">blog post</a> about different compressors for Django Redis.</p>", "url": "https://www.peterbe.com/plog/best-django-redis-configuration-for-speed-and-size", "disallow_comments": false, "split": null}, {"oid": "best-django-redis-configuration-for-speed-and-size", "title": "Best Django Redis configuration for speed and size", "pub_date": "2026-07-19T20:01:27.016Z", "comments": 0, "categories": ["Django", "Python"], "html": "<p>In 2017 I wrote <a href=\"/plog/fastest-redis-optimization-for-django\">\"Fastest Redis configuration for Django\"</a>. Now, I've made an update for 2026 that focuses on comparing default Redis with three compressors:</p>\n<ul>\n<li><code>zlib</code></li>\n<li><code>lzma</code></li>\n<li><code>zstd</code></li>\n</ul>\n<p>The results on macOS are:</p>\n<pre><code>                        TIMES        AVERAGE   MEDIAN (P50)   MEDIAN (P90)         STDDEV\ndefault                    198        0.181ms        0.176ms        0.216ms        0.038ms\nzlib                       213        0.190ms        0.182ms        0.237ms        0.039ms\nlzma                       134        0.273ms        0.272ms        0.332ms        0.042ms\nzstd                       202        0.192ms        0.180ms        0.243ms        0.046ms\n\nBest Means (shorter better)\n###############################################################################\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588                        0.181  default\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588                      0.190  zlib\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588  0.273  lzma\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588                     0.192  zstd\n\nBest Medians (shorter better)\n###############################################################################\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588                         0.176  default\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588                       0.182  zlib\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588  0.272  lzma\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588                        0.180  zstd\n\nSize of Data Saved (shorter better)\n###############################################################################\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588  5151  default\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588                                        2151  zlib\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588                                                 1420  lzma\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588                                          2010  zstd\n\nSize of Data without Default (shorter better)\n###############################################################################\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588  2151  zlib\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588                         1420  lzma\n\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588       2010  zstd\n</code></pre>\n<p>The \"conclusion\", on macOS, is that <code>lzma</code> is slowest but compresses the best. On Ubuntu (in GitHub Actions), the fastest of the compressors is <code>zstd</code> and the best compression is <code>lzma</code>.</p>\n<p>But it's a very small difference.</p>\n<p>The type of data you store in Redis will matter for <em>your</em> use case. In this experiment, it's storing strings of numbers. Each number is about 10 characters long.</p>", "url": null, "disallow_comments": false, "split": null}, {"oid": "how-to-use-a-listtuplearray-in-django-with-a-raw-sql-cursor", "title": "How to use a list/tuple/array in Django with a raw SQL cursor", "pub_date": "2026-07-14T13:14:40.383Z", "comments": 0, "categories": ["Django", "Python"], "html": "<p>This does <strong><em>not</em></strong> work:</p>\n<pre><code class=\"hljs\">\n<span class=\"hljs-keyword\">from</span> django.db <span class=\"hljs-keyword\">import</span> connection\n\nlist_of_values = [<span class=\"hljs-number\">1</span>, <span class=\"hljs-number\">2</span>, <span class=\"hljs-number\">3</span>]\n<span class=\"hljs-keyword\">with</span> connection.cursor() <span class=\"hljs-keyword\">as</span> cursor:\n    cursor.execute(<span class=\"hljs-string\">&quot;&quot;&quot;\n        SELECT *\n        FROM my_model_table\n        WHERE some_value IN %s\n    &quot;&quot;&quot;</span>, [\n        <span class=\"hljs-built_in\">tuple</span>(list_of_values),\n    ])\n    results = cursor.fetchall()\n</code></pre>\n\n<p>It will give you:</p>\n<pre>django.db.utils.ProgrammingError: syntax error at or near &quot;&#x27;(1,2,3)&#x27;&quot;\nLINE 4:         WHERE id IN &#x27;(1,2,3)&#x27;</pre>\n\n<p>It <em>used</em> to work with <code>psycopg</code> v2. Now, in psycopg v3, you have to use the <code>ANY</code> operator. See <a href=\"https://www.psycopg.org/psycopg3/docs/basic/from_pg2.html#you-cannot-use-in-s-with-a-tuple\">\"You cannot use IN %s with a tuple\"</a></p>\n<p>This <em>will</em> work:</p>\n<pre><code class=\"hljs\">\n<span class=\"hljs-keyword\">from</span> django.db <span class=\"hljs-keyword\">import</span> connection\n\nlist_of_values = [<span class=\"hljs-number\">1</span>, <span class=\"hljs-number\">2</span>, <span class=\"hljs-number\">3</span>]\n<span class=\"hljs-keyword\">with</span> connection.cursor() <span class=\"hljs-keyword\">as</span> cursor:\n    cursor.execute(\n        <span class=\"hljs-string\">&quot;&quot;&quot;\n        SELECT *\n        FROM my_model_table\n        WHERE some_value = ANY(%s)\n    &quot;&quot;&quot;</span>,\n        [\n            list_of_values,\n        ],\n    )\n    results = cursor.fetchall()\n</code></pre>\n\n<p>Note the <code>ANY(%s)</code>, and instead of a list that has a tuple, it's a list that has a list.</p>\n<h4 id=\"what-about-a-list-of-strings\"><a class=\"toclink\" href=\"#what-about-a-list-of-strings\">What About a List of Strings</a></h4>\n<p>Consider...</p>\n<pre><code class=\"hljs\">\nfrom django.db import connection\n\n<span class=\"hljs-deletion\">-list_of_values = [1, 2, 3]</span>\n<span class=\"hljs-addition\">+list_of_values = [&#x27;foo&#x27;, &#x27;bar&#x27;, &#x27;fiz&#x27;]</span>\nwith connection.cursor() as cursor:\n    cursor.execute(\n        &quot;&quot;&quot;\n        SELECT *\n        FROM my_model_table\n        WHERE some_value = ANY(%s)\n    &quot;&quot;&quot;,\n        [\n            list_of_values,\n        ],\n    )\n    results = cursor.fetchall()\n</code></pre>\n\n<p>That will result in:</p>\n<pre>django.db.utils.DataError: invalid input syntax for type integer: &quot;foo&quot;\nLINE 4:         WHERE some_value = ANY(&#x27;{foo,bar,fiz}&#x27;)</pre>\n\n<p>My solution was to rewrite the SQL string itself and treat each value as a parameter each. In other words, the SQL string, before being sent to <code>cursor.execute(...)</code> will contain something like this:</p>\n<pre><code class=\"hljs\">\n<span class=\"hljs-keyword\">AND</span> (\n  some_value <span class=\"hljs-operator\">=</span> <span class=\"hljs-operator\">%</span> <span class=\"hljs-keyword\">OR</span>\n  some_value <span class=\"hljs-operator\">=</span> <span class=\"hljs-operator\">%</span> <span class=\"hljs-keyword\">OR</span>\n  some_value <span class=\"hljs-operator\">=</span> <span class=\"hljs-operator\">%</span> <span class=\"hljs-keyword\">OR</span>\n  some_value <span class=\"hljs-operator\">=</span> <span class=\"hljs-operator\">%</span> <span class=\"hljs-keyword\">OR</span>\n  <span class=\"hljs-comment\">-- ...etc...</span>\n  some_value <span class=\"hljs-operator\">=</span> <span class=\"hljs-operator\">%</span>\n)\n</code></pre>\n\n<p>This will work and is safe:</p>\n<pre><code class=\"hljs\">\n<span class=\"hljs-keyword\">from</span> django.db <span class=\"hljs-keyword\">import</span> connection\n\nlist_of_values = [<span class=\"hljs-string\">&quot;foo&quot;</span>, <span class=\"hljs-string\">&quot;bar&quot;</span>, <span class=\"hljs-string\">&quot;fiz&quot;</span>]\n<span class=\"hljs-keyword\">with</span> connection.cursor() <span class=\"hljs-keyword\">as</span> cursor:\n    cursor.execute(\n        <span class=\"hljs-string\">f&quot;&quot;&quot;\n        SELECT *\n        FROM my_model_table\n        WHERE (<span class=\"hljs-subst\">{<span class=\"hljs-string\">&quot; OR &quot;</span>.join([<span class=\"hljs-string\">&quot;some_value = %s&quot;</span> <span class=\"hljs-keyword\">for</span> _ <span class=\"hljs-keyword\">in</span> list_of_values])}</span>)\n    &quot;&quot;&quot;</span>,\n        list_of_values,\n    )\n    results = cursor.fetchall()\n</code></pre>", "url": null, "disallow_comments": false, "split": null}, {"oid": "driving-range-and-thunder-clouds", "title": "Driving range and thunder clouds", "pub_date": "2026-07-12T10:26:11.412Z", "comments": 0, "categories": [], "html": "", "url": null, "disallow_comments": false, "split": null}, {"oid": "4th-of-july-tablecloth-that-we-all-contributed-to", "title": "4th of July table cloth that we all contributed to", "pub_date": "2026-07-04T22:19:09.620Z", "comments": 0, "categories": [], "html": "", "url": null, "disallow_comments": false, "split": null}, {"oid": "potpourri-of-funly-cut-hot-dogs", "title": "Potpourri of fun\u2019ly cut hot dogs", "pub_date": "2026-07-04T20:28:04.320Z", "comments": 0, "categories": [], "html": "", "url": null, "disallow_comments": false, "split": null}, {"oid": "putted-in-my-first-ever-eagle", "title": "Putted in my first ever Eagle!", "pub_date": "2026-06-28T17:18:19.306Z", "comments": 0, "categories": [], "html": "", "url": null, "disallow_comments": false, "split": null}, {"oid": "celebratory-1776-titleist", "title": "Celebratory 1776 Titleist", "pub_date": "2026-06-28T17:15:25.102Z", "comments": 0, "categories": [], "html": "<p>Unfortunately, I hooked this one into the water on the seventh hole.</p>", "url": null, "disallow_comments": false, "split": null}, {"oid": "a-little-praying-mantis-climbing-on-the-screen-on-my-back-porch", "title": "A little praying mantis climbing on the screen on my back porch", "pub_date": "2026-06-25T00:07:45.134Z", "comments": 0, "categories": [], "html": "", "url": null, "disallow_comments": false, "split": null}]}