chiark / gitweb /
Bodgy fix for poor performance of wcwidth.
authorSimon Tatham <anakin@pobox.com>
Fri, 8 Dec 2023 17:56:45 +0000 (17:56 +0000)
committerSimon Tatham <anakin@pobox.com>
Fri, 8 Dec 2023 17:56:45 +0000 (17:56 +0000)
Mastodonochrome was running rather slowly, and when I profiled it, it
turned out the bottleneck was the 'wcwidth' Python module, which is
not a wrapper on the C function, but a reimplementation in pure Python
and therefore not very fast.

I've bodged it by just making one big cache of all the strings that
the program ever passes to wcwidth, since there were only two calls to
that function at all and they were both in text.py. I don't know if
that's the right fix in the long term, but it seems to have improved
things considerably for now.

(Also, while I'm at it, ColouredString now wcwidths its contents once
at construction time and doesn't redo it on every call, so it doesn't
even look up the same thing in the cache multiple times.)

text.py

diff --git a/text.py b/text.py
index c0cd5666d10023bddbe0d7063bbeb28be7a33bbf..de68152b42b09068bb2af9026c4a05f30cc5af8a 100644 (file)
--- a/text.py
+++ b/text.py
@@ -35,6 +35,12 @@ colourmap = {
     '-': [0, 7, 40, 36], # separator line between editor header and content
 }
 
+wcswidth_cache = {}
+def cached_wcswidth(s):
+    if s not in wcswidth_cache:
+        wcswidth_cache[s] = wcwidth.wcswidth(s)
+    return wcswidth_cache[s]
+
 class ColouredString:
     def __init__(self, string, colour=' '):
         if isinstance(string, ColouredString):
@@ -44,6 +50,7 @@ class ColouredString:
                 assert len(colour) == 1, "Colour ids are single characters"
                 colour = colour * len(string)
             self.s, self.c = string, colour
+        self.width = cached_wcswidth(self.s)
 
     def __add__(self, rhs):
         rhs = type(self)(rhs)
@@ -55,10 +62,6 @@ class ColouredString:
     def __len__(self):
         return len(self.s)
 
-    @property
-    def width(self):
-        return wcwidth.wcswidth(self.s)
-
     def __str__(self):
         return self.s
 
@@ -97,7 +100,7 @@ class ColouredString:
             colour = self.c[pos]
             fraglen = len(self.c) - pos - len(self.c[pos:].lstrip(colour))
             frag = self.s[pos:pos+fraglen]
-            yield frag, colour, wcwidth.wcswidth(frag)
+            yield frag, colour, cached_wcswidth(frag)
             pos += fraglen
 
     def split(self, width):