* * * * * Unicode. Why did it have to be Unicode? Well, I have my answer [1]. I first found a smaller request that exhibits the behavior as to not generate half a million lines of output: Table: Lines of code executed for a small request gopher 2549 gemini 564 Good. Two and a half thousand lines of code is tractable. Now, just to show how easy it is to profile Lua code, here's the code I'm using for my gopher server [2]: -----[ Lua ]----- local profile = {} local function doprofile() local info = debug.getinfo(2) local name = info.name or "..." local file = info.source or "@" local key = string.format("%s$%s(%d)",file,name,info.currentline) if not profile[key] then profile[key] = 1 else profile[key] = profile[key] + 1 end end -----[ END OF LINE ]----- For each line of code executed, we get the filename, the function name and the line of code that's executing, turn that into a key, and use that to count the number of times that line of code is executed. Easy. And then some code to dump the results: -----[ Lua ]----- local function cleanup() local results = {} for name,value in pairs(profile) do results[#results + 1] = { file = name , count = value } end table.sort(results,function(a,b) if a.count > b.count then return true elseif a.count < b.count then return false else return a.file < b.file end end) local f = io.open("/tmp/dump.txt","w") for i = 1 , #results do f:write(string.format("%6d %s\n",results[i].count,results[i].file)) end f:close() end -----[ END OF LINE ]----- We sort the results based on line count, then alphabetically by key. And like before: -----[ Lua ]----- local function main(iostream) debug.sethook(doprofile,'line') -- The rest of the main code debug.sethook() cleanup() end -----[ END OF LINE ]----- I make the request and get some results: -----[ data ]----- 215 @/usr/local/share/lua/5.4/org/conman/string.lua$wrapt(202) 211 @/usr/local/share/lua/5.4/org/conman/string.lua$wrapt(203) 211 @/usr/local/share/lua/5.4/org/conman/string.lua$wrapt(204) 211 @/usr/local/share/lua/5.4/org/conman/string.lua$wrapt(268) 210 @/usr/local/share/lua/5.4/org/conman/string.lua$wrapt(282) 210 @/usr/local/share/lua/5.4/org/conman/string.lua$wrapt(283) 169 @/usr/local/share/lua/5.4/org/conman/string.lua$wrapt(219) 169 @/usr/local/share/lua/5.4/org/conman/string.lua$wrapt(224) 169 @/usr/local/share/lua/5.4/org/conman/string.lua$wrapt(239) 169 @/usr/local/share/lua/5.4/org/conman/string.lua$wrapt(240) 42 @/usr/local/share/lua/5.4/org/conman/string.lua$wrapt(205) 42 @/usr/local/share/lua/5.4/org/conman/string.lua$wrapt(206) 42 @/usr/local/share/lua/5.4/org/conman/string.lua$wrapt(207) 17 @port70.lua$...(272) 17 @port70.lua$...(273) 9 @/usr/local/share/lua/5.4/org/conman/net/ios.lua$write(547) ... -----[ END OF LINE ]----- Oh. Yeah. That. Obvious in hindsight. I completely forgot about that. Okay. The function in question, wrapt(), wraps text [3] and it's a rather heavy function due to Unicode [4] (and I'm not even following the full specification there). This is the major difference between the gopher and Gemini servers—I don't wrap text for Gemini (the clients handle that). I guess I'll have to drop down to C if I want to speed this up. Sigh. [1] gopher://gopher.conman.org/0Phlog:2024/05/30.1 [2] https://github.com/spc476/port70 [3] https://github.com/spc476/lua-conmanorg/blob/4ebd6da4f82617bf87a9f6c5a0d9eb5f4f96578f/lua/string.lua#L193 [4] https://www.unicode.org/reports/tr14/ Email Sean Conner at sean@conman.org .