Ticket UUID: | 8da7af2f8e8975de3acd0b4f4d45bf1d69bc7197 | |||
Title: | slow widget creation if default font is not used | |||
Type: | Bug | Version: | 8.6 | |
Submitter: | anonymous | Created on: | 2024-01-06 17:17:55 | |
Subsystem: | 44. Generic Fonts | Assigned To: | nobody | |
Priority: | 5 Medium | Severity: | Important | |
Status: | Open | Last Modified: | 2024-06-24 06:02:24 | |
Resolution: | None | Closed By: | nobody | |
Closed on: | 2024-01-27 13:50:34 | |||
Description: |
If all widgets are created with a -font parameter, widget creation is very slow. This is slow: grid [ ttk::entry .myEntry01 -font TkFixedFont -width 35 -textvariable myvariable ] grid [ ttk::entry .myEntry02 -font TkFixedFont -width 35 -textvariable myvariable ] grid [ ttk::entry .myEntry03 -font TkFixedFont -width 35 -textvariable myvariable ] set myvariable "Hello World" this is quick: grid [ ttk::entry .myEntry00 -width 35 -textvariable myvariable ] grid [ ttk::entry .myEntry01 -font TkFixedFont -width 35 -textvariable myvariable ] grid [ ttk::entry .myEntry02 -font TkFixedFont -width 35 -textvariable myvariable ] grid [ ttk::entry .myEntry03 -font TkFixedFont -width 35 -textvariable myvariable ] set myvariable "Hello World" I think the problem manifests because: * Tk instantiates every widget with the default font. * It then sets the font according to the -font parameter. * if the default font isn't used by any other widget, it's unloaded. The next widget will go through the same process. Since the default font got unloaded during the instanciation of the former widget, it will be loaded from disk again. I think, the relevant source is at ttkWidget.c: TtkWidgetConstructorObjCmd(..) [...] /// I think Tk_InitOptions sets default font if (Tk_InitOptions(interp, recordPtr, optionTable, tkwin) != TCL_OK) { goto error; } /// I think this then sets the other font, forgetting the default font if (Tk_SetOptions(interp, recordPtr, optionTable, objc - 2, objv + 2, tkwin, &savedOptions, NULL) != TCL_OK) { Tk_RestoreSavedOptions(&savedOptions); goto error; } [...] | |||
User Comments: |
fvogel added on 2024-06-24 06:02:24:
I understand the idea. Note however that:
jan.nijtmans added on 2024-06-23 21:38:40: How about adding a "font" field to the TkDisplay struct, and initialize it with the default font. When the TkDisplay is destructed, destroy the default font too. Then there is always at least one reference to the default font. (I could imagine that each display might have its own default font, that's why this probably is the best place) fvogel added on 2024-06-23 08:19:29: Sorry, I have to reopen this because my "fix" is wrong. Please see [a0dca39fd1] for its consequences. After careful analysis, the cacheing mechanism of the TkFont pointer in the Tcl_Obj internalRep.twoPtrValue.ptr1 (see Tk_AllocFontFromObj and Tk_GetFontFromObj) does after all seem to be totally correct to me. As a consequence I had to revert [f45960ae] in [921bf608]. What's happening is exactly what the OP described in the first place: * Tk creates every widget with the default font (through Tk_InitOptions). * It then sets the font according to the -font parameter (through Tk_SetOptions). * If the default font isn't used by any other widget, it's freed (through Tk_FreeSavedOptions). Then, creating many widgets with a -font option leads to a lot of allocation and freeing of the default font. This takes time. We have this Tk_InitOptions --> Tk_SetOptions --> Tk_FreeSavedOptions pattern everywhere in Tk (see for instance ConfigureEntry in tkEntry.c, for regular (not ttk) entry widgets). I don't readily see how we can avoid the problem. fvogel added on 2024-01-27 13:50:34: Thank you for the feedback, and for the report in the first place. Fix is now merged. anonymous added on 2024-01-23 20:18:11: The fix works for me also. Thanks! fvogel added on 2024-01-22 21:49:11: The two new failures in the test suite come from the interaction between config.test (config-4.47 and config-4.48) and font.test (font-17.1 and font-18.1). All use the {Courier 12} font. When running only font.test no previous use of {Courier 12} happened --> the testfont counts are OK. When config.test is run before font.test, the testfont counts takes into account the use of {Courier 12} in config.test --> the counts in font.test are no longer the expected ones. My fix [d7b0c443] reveals the issue with the test suite hygiene, which is now fixed by [07f1820b46]. fvogel added on 2024-01-22 21:06:44: There are two new failures in the test suite: font-17.1 and font-18.1. When running the font.test file only they do not fail. They fail when the full test suite is run. This indicates bad test suite hygiene rather than a problem with the fix. I'll investigate. fvogel added on 2024-01-21 21:19:46: I have traced this performance problem to be due to a bug in the font cache handling in Tk. Please see branch bug-8da7af2f8e. Timings are now the same for me with or without .myEntry00 (checked on Linux). Could you please confirm whether you see the issue as fixed for you too? anonymous added on 2024-01-14 20:21:51: .. ok sorry, just realized that removing the textvariable gets rid of the long teardown time and the problem becomes more clear: #!/usr/bin/wish ttk::entry .myEntry00 for {set i 0} {$i < 2000} {incr i} { ttk::entry .myEntry$i -font TkFixedFont } exit time with myEntry00: 0.219s time w/o myEntry00: 26.091s anonymous added on 2024-01-14 20:10:04: Indeed gridding isn't necessary to reproduce, timings are very similar. If I run this script in linux #!/usr/bin/wish set myvariable "Hello World" grid [ ttk::entry .myEntry00 -width 35 -textvariable myvariable ] for {set i 0} {$i < 2000} {incr i} { grid [ ttk::entry .myEntry$i -font TkFixedFont -width 35 -textvariable myvariable ] } exit .. with time like so: time ./script.tcl I get: - 11s when I create myEntry00 first (with the standard font). - 36s when I comment out creation of myEntry00 The problem is more severe than the timings suggest: in both cases 10s are spent tearing down the window. Accounting for that, the difference in widget creation is roughly 25s to 1s. For me, the effect is clearly visible without timing: If I don't put the exit at the end of the script, such that I actually see the contents of the window, the quick versions comes up almost immediately. The slow version takes felt 20s before anything is shown. fvogel added on 2024-01-13 14:40:10: > That's a difference of ~7%. Sorry, it's more than that. Still, I can't reproduce the original numbers I saw. fvogel added on 2024-01-13 14:31:47: Hmmm... I'm no longer seeing a performance difference of 3x. I can't reproduce the timings I pasted below. With 5000 widgets (see my attached test script), on my hardware running the current core-8-6-branch on Windows 11, proc test1_slow executes in 4 seconds and proc test2_quick in 2.8 seconds. That's a difference of ~7%. You (the OP) says "If all widgets are created with a -font parameter, widget creation is very slow." Could you please better characterize what you mean by "very slow"? What are your numbers? Finally, gridding should not be needed to show the difference if it really lies in the Tk_InitOptions/Tk_SetOptions calls, and the -width and -textvariable options neither. This means the test script should be simplifiable. fvogel added on 2024-01-08 23:17:43: Profiling of proc test1_slow (see the attached script) on Windows shows that 72 % of the time is indeed spent in TtkWidgetConstructorObjCmd(). This time is spent in three main contributors in TtkWidgetConstructorObjCmd():
When profiling proc test2_quick, 69% of the time is spent in TtkWidgetConstructorObjCmd(), with 54 % in Tk_MakeWindowExist(). Tk_InitOptions() indeed has negligible time (1%) spent in it. Therefore, while Tk_MakeWindowExist() is the first contributor to performance, one could perhaps look at Tk_InitOptions(). Here are my timings: % destroy {*}[winfo children .] % time {test1_slow $nw} 10992699 microseconds per iteration % destroy {*}[winfo children .] % time {test2_quick $nw} 3585076 microseconds per iteration The slowdown of proc test1_slow compared to proc test2_quick is of a factor of 3. |
Attachments:
- 8da7af2f8e - slow widget creation if default font is not used.tcl [download] added by fvogel on 2024-01-08 23:18:53. [details]