{"id":20073,"date":"2008-11-26T07:00:00","date_gmt":"2008-11-26T15:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2008\/11\/26\/the-cost-benefit-analysis-of-bitfields-for-a-collection-of-booleans\/"},"modified":"2008-11-26T07:00:00","modified_gmt":"2008-11-26T15:00:00","slug":"the-cost-benefit-analysis-of-bitfields-for-a-collection-of-booleans","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20081126-00\/?p=20073","title":{"rendered":"The cost-benefit analysis of bitfields for a collection of booleans"},"content":{"rendered":"<p><P>\nConsider a class with\na bunch of <CODE>BOOL<\/CODE> members:\n<\/P>\n<PRE>\n\/\/ no nitpicking over BOOL vs bool allowed\nclass Pear {\n &#8230;\n BOOL m_peeled;\n BOOL m_sliced;\n BOOL m_pitted;\n BOOL m_rotten;\n &#8230;\n};\n<\/PRE>\n<P>\nYou might be tempted to convert the <CODE>BOOL<\/CODE> fields\ninto bitfields:\n<PRE>\nclass Pear {\n &#8230;\n BOOL m_peeled:1;\n BOOL m_sliced:1;\n BOOL m_pitted:1;\n BOOL m_rotten:1;\n &#8230;\n};\n<\/PRE>\n<P>\nSince a <CODE>BOOL<\/CODE> is typedef&#8217;d as <I>INT<\/I> (which on\nWindows platforms is a signed 32-bit integer),\nthis takes sixteen bytes and packs them into one.\nThat&#8217;s a 93% savings!\nWho could complain about that?\n<\/P>\n<P>\nHow much did that savings cost you, and how much did you save anyway?\n<\/P>\n<P>\nLet&#8217;s look at the cost of that savings.\nCode that updated the plain <CODE>BOOL<\/CODE>\n<CODE>m_sliced<\/CODE> member could do it by simply storing the result\ninto the member.\nSince it was a normal field,\nthis could be accomplished directly:\n<\/P>\n<PRE>\n  mov [ebx+01Ch], eax ; m_sliced = sliced\n<\/PRE>\n<P>\nOn the other hand, when it&#8217;s a bitfield, updating it becomes trickier:\n<\/P>\n<PRE>\n  add eax, eax        ; shift &#8220;sliced&#8221; into the correct position\n  xor eax, [ebx+01Ch] ; merge &#8220;sliced&#8221; with other bits\n  and eax, 2\n  xor [ebx+01Ch], eax ; store the new bitfield\n<\/PRE>\n<P>\n<B>Exercise<\/B>: Figure out how the above trick works.\n<\/P>\n<P>\nConverting a <CODE>BOOL<\/CODE> to a single-bit field saved three bytes\nof data but cost you eight bytes of code when the member is assigned\na non-constant value.\nSimilarly, extracting the value gets more expensive.\nWhat used to be\n<\/P>\n<PRE>\n push [ebx+01Ch]      ; m_sliced\n call _Something@4    ; Something(m_sliced);\n<\/PRE>\n<P>\nbecomes\n<\/P>\n<PRE>\n mov  ecx, [ebx+01Ch] ; load bitfield value\n shl  ecx, 30         ; put bit at top\n sar  ecx, 31         ; move down and sign extend\n push ecx\n call _Something@4    ; Something(m_sliced);\n<\/PRE>\n<P>\nThe bitfield version is bigger by nine bytes.\n<\/P>\n<P>\nLet&#8217;s sit down and do some arithmetic.\nSuppose each of these bitfielded fields is accessed six times\nin your code, three times for writing and three times for reading.\nThe cost in code growth is approximately 100 bytes.\nIt won&#8217;t be exactly 102 bytes because the optimizer may be able\nto take advantage of values already in registers for some operations,\nand the additional instructions may have hidden costs in terms of\nreduced register flexibility.\nThe actual difference may be more, it may be less, but for a\nback-of-the-envelope calculation let&#8217;s call it 100.\nMeanwhile,\nthe memory savings was 15 byte per class.\nTherefore, the breakeven point is seven.\nIf your program creates fewer than seven instances of this class,\nthen the code cost exceeds the data savings: Your memory optimization\nwas a memory de-optimization.\n<\/P>\n<P>\nEven if you manage to come out ahead in the accounting ledger,\nit may be a win of just a few hundred bytes.\nThat&#8217;s an awful lot of extra hassle to save a few hundred bytes.\nAll somebody has to do is add an icon to a dialog box and your\nsavings will vanish.\n<\/P>\n<P>\nWhen I see people making these sorts of\n<A HREF=\"http:\/\/blogs.msdn.com\/tonyschr\/archive\/2006\/01\/24\/517088.aspx\">\nmicro-optimizations<\/A>,\nsometimes I&#8217;ll ask them,\n&#8220;How many instances of this class does the program create?&#8221;\nand sometimes the response will be,\n&#8220;Oh, maybe a half dozen. Why do you ask?&#8221;\n<\/P>\n<P>\nBut wait, there&#8217;s more.\nPacking all these members into a bitfield has other costs.\nYou lose the ability to set a hardware write breakpoint on a specific bit,\nsince hardware breakpoints are done at the byte level (at a minimum).\nYou also lose atomicity:\nAn update to <CODE>m_sliced<\/CODE> will interfere with a simultaneous\nupdate to <CODE>m_peeled<\/CODE> on another thread,\nsince the update process merges the two values and stores the result\nnon-atomically.\n(Note that you also lose atomicity if you had used a byte-sized\n<CODE>bool<\/CODE> instead of a 32-bit <CODE>BOOL<\/CODE> because some CPU\narchitectures such as the original Alpha AXP cannot access memory\nin units smaller than a <CODE>DWORD<\/CODE>.)\n<\/P>\n<P>\nThese are just a few things to take into account when considering\nwhether you should change your fields to bitfields.\nSure, bitfields save data memory, but you have to balance it\nagainst the cost in code size, debuggability, and reduced multithreading.\nIf your class is going to be instantiated only a few times\n(and by &#8220;a few&#8221; I&#8217;m thinking less than a few thousand times),\nthen these costs most likely exceed the savings.\n<\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>How many of them are there?<\/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-20073","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>How many of them are there?<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/20073","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=20073"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/20073\/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=20073"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=20073"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=20073"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}