{"id":110679,"date":"2024-12-26T07:00:00","date_gmt":"2024-12-26T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110679"},"modified":"2024-12-24T07:05:22","modified_gmt":"2024-12-24T15:05:22","slug":"20241226-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20241226-00\/?p=110679","title":{"rendered":"Why are Win32 resources strings bundled at all? And why bundles of 16?"},"content":{"rendered":"<p>We saw some time ago that <a title=\"The format of string resources\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040130-00\/?p=40813\"> strings in Win32 resources are grouped in bundles of 16<\/a>. Why not have each string be a separate resource? Why are they bundled at all? And why bundles of 16? Why not 15 or 8 or 32?<\/p>\n<p>Recall <a title=\"The management of memory for resources in 16-bit Windows\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040202-00\/?p=40783\"> how resources worked in 16-bit Windows<\/a>. To load a resource, you allocate a selector to hold the resource data, load the resource data from disk into the selector, and then use the selector to access the resource. The selector remains in memory afterward, but it is marked as &#8220;discardable&#8221;, so that it can be freed when the system comes under memory pressure.<\/p>\n<p>Windows 1.0&#8217;s system requirements did not include a hard drive. You could run it off a two-floppy system.\u00b9 Reducing I\/O had a noticeable effect on performance, so issuing a separate I\/O for each string was going to be inefficient.<\/p>\n<p>On top of that, <a title=\"How did real-mode Windows patch up return addresses to discarded code segments?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20120629-00\/?p=7253\"> Windows 1.0 had a limit of 4096 selectors<\/a>, so putting each string in its own resource would drain the system of selectors.<\/p>\n<p>On the other hand, you don&#8217;t want to load <i>all<\/i> the strings, because that means doing a large I\/O transfer for data, most of which are not going to be used. Furthermore, you increased memory consumption because all of the strings got loaded into memory, creating additional memory pressure, and when the strings were discarded (which would happen more often because of the increased memory pressure), you lost all of your strings.<\/p>\n<p>The decision to bundle strings in groups of 16 was an attempt to balance these two competing performance issues. There&#8217;s nothing magical about the number 16. It was a convenient number that gave you a decent amount of batching while still keeping the batches from getting too large.<\/p>\n<p>Grouping your strings into bundles became a performance game similar to <a title=\"I wrote FAT on an airplane, for heaven's sake\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20131008-00\/?p=3003\"> segment tuning<\/a>, where you wanted to put strings that were used at the same time into the same bundle to maximize the value of each I\/O operation.<\/p>\n<p>Although 32-bit Windows doesn&#8217;t use allocate resources to segments the same way that 16-bit Windows did, the bundling design was nevertheless carried forward. One reason is that bundling <a title=\"What is the highest numerical resource ID permitted by Win32?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20110217-00\/?p=11463\"> expanded the range of string resource IDs from a 16-bit value to a 20-bit value<\/a>, so the highest resource string ID went from 65535 to a bit over a million. And even though they don&#8217;t occupy a segment any more, there is still overhead in the file format to describe each resource. Strings tend to be short, so this overhead ends up being significant. You don&#8217;t want a four-character string to come with 24 bytes of overhead. There is still a small memory benefit to not wasting slots in a bundle, though it is not as severe as it was in 16-bit days.<\/p>\n<p>\u00b9 That&#8217;s what I did back in the day. The company I worked for at the time had one computer with a hard drive, and wow that hard drive made a big difference.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Balancing multiple performance factors.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-110679","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Balancing multiple performance factors.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110679","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=110679"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110679\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=110679"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110679"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110679"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}