{"id":32233,"date":"2006-02-20T10:00:00","date_gmt":"2006-02-20T10:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2006\/02\/20\/why-does-my-program-run-faster-if-i-click-and-hold-the-caption-bar\/"},"modified":"2006-02-20T10:00:00","modified_gmt":"2006-02-20T10:00:00","slug":"why-does-my-program-run-faster-if-i-click-and-hold-the-caption-bar","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20060220-00\/?p=32233","title":{"rendered":"Why does my program run faster if I click and hold the caption bar?"},"content":{"rendered":"<p>\nSometimes, people discover that a long-running task\nruns faster if you hold down the mouse.\nHow can that be?\n<\/p>\n<p>\nThis strange state of affairs\ntypically results when a program is spending too much time\nupdating its progress status and not enough time actually doing work.\n(In other words, the programmer messed up badly.)\nWhen you click and hold the mouse over the caption bar,\nthe window manager waits for the next mouse message\nso it can determine whether\nyou are clicking on the caption or attempting to drag.\nDuring this waiting, window painting is momentarily suppressed.\n<\/p>\n<p>\nThat&#8217;s why the program runs faster:\nNo window painting means less CPU spent updating something\nfaster than you can read it anyway.\nLet&#8217;s illustrate this with a sample program.\nStart with\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2005\/04\/22\/410773.aspx\">\nthe new scratch program<\/a>\nand make the following changes:\n<\/p>\n<pre>\nclass RootWindow : public Window\n{\npublic:\n virtual LPCTSTR ClassName() { return TEXT(\"Scratch\"); }\n static RootWindow *Create();\n <font COLOR=\"blue\">void PaintContent(PAINTSTRUCT *pps);<\/font>\nprotected:\n LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);\n LRESULT OnCreate();\n <font COLOR=\"blue\">static DWORD CALLBACK ThreadProc(void *p);<\/font>\nprivate:\n HWND m_hwndChild;\n <font COLOR=\"blue\">int m_value;<\/font>\n};\nLRESULT RootWindow::OnCreate()\n{\n <font COLOR=\"blue\">QueueUserWorkItem(ThreadProc, this, WT_EXECUTELONGFUNCTION);<\/font>\n return 0;\n}\n<font COLOR=\"blue\">void RootWindow::PaintContent(PAINTSTRUCT *pps)\n{\n TCHAR sz[256];\n int cch = wnsprintf(sz, 256, TEXT(\"%d\"), m_value);\n ExtTextOut(pps-&gt;hdc, 0, 0, 0, &amp;pps-&gt;rcPaint, sz, cch, 0);\n}\nDWORD RootWindow::ThreadProc(void *p)\n{\n RootWindow*self = reinterpret_cast&lt;RootWindow*&gt;(p);\n for (int i = 0; i &lt; 100000; i++) {\n  self-&gt;m_value++;\n  InvalidateRect(self-&gt;m_hwnd, NULL, NULL);\n }\n MessageBeep(-1);\n return 0;\n}<\/font>\n<\/pre>\n<p>\nThis program fires up a background thread that counts up to 100,000\nand invalidates the foreground window each time the value changes.\nRun it and watch how fast the numbers count up to 100,000.\n(I added a little beep when the loop is finished so you can judge\nthe time by listening.)\n<\/p>\n<p>\nNow run it again, but this time, click and hold the mouse on the title bar.\nNotice that the program beeps almost immediately:\nIt ran faster when you held the mouse down.\nThat&#8217;s because all the painting was suppressed by the\nmaybe-a-drag-operation-is-in-progress that was triggered when you\nclicked and held the caption.\n<\/p>\n<p>\nUpdating the screen at every increment is clearly pointless\nbecause you&#8217;re incrementing far faster than the screen can refresh,\nnot to mention far faster than the human eye can read it.\nAs a rule of thumb, changing progress status\nfaster than ten times per second is generally pointless.\nThe effort you&#8217;re spending on the screen updates is wasted.\n<\/p>\n<p>\nLet&#8217;s fix our sample program to update at most ten times per second.\nWe will run a timer at 100ms which checks if anything has changed\nand repaints if so.\n<\/p>\n<pre>\nclass RootWindow : public Window\n{\n ...\n <font COLOR=\"blue\">LONG m_fChanged;<\/font>\n};\nDWORD RootWindow::ThreadProc(void *p)\n{\n RootWindow*self = reinterpret_cast&lt;RootWindow*&gt;(p);\n for (int i = 0; i &lt; 100000; i++) {\n  self-&gt;m_value++;\n  <font COLOR=\"blue\">InterlockedCompareExchangeRelease(&amp;m_fChanged, TRUE, FALSE);<\/font>\n }\n MessageBeep(-1);\n return 0;\n}\nLRESULT RootWindow::OnCreate()\n{\n QueueUserWorkItem(ThreadProc, this, WT_EXECUTELONGFUNCTION);\n <font COLOR=\"blue\">SetTimer(m_hwnd, 1, 100, NULL);<\/font>\n return 0;\n}\nLRESULT RootWindow::HandleMessage(\n                          UINT uMsg, WPARAM wParam, LPARAM lParam)\n{\n  ...\n  <font COLOR=\"blue\">case WM_TIMER:\n   switch (wParam) {\n   case 1:\n    if (InterlockedCompareExchangeAcquire(&amp;m_fChanged,\n                                                   FALSE, TRUE)) {\n      if (m_value &gt;= 100000) {\n        KillTimer(m_hwnd, 1);\n      }\n      InvalidateRect(m_hwnd, NULL, FALSE);\n    }\n   }\n   break;<\/font>\n   ...\n}\n<\/pre>\n<p>\nInstead of updating the screen each time the counter changes value,\nwe merely set a &#8220;hey, something changed&#8221; flag and check it on our\ntimer.\nWe set the flag with release semantics in the producer thread\n(because we want all pending stores to complete before the exchange\noccurs) and clear the flag with acquire semantics in the consumer\nthread\n(because we don&#8217;t want any future stores to be speculated ahead\nof the exchange).\n<\/p>\n<p>\nRun the program again and notice that it counts all the way\nup to 100,000 instantly.\nOf course, that doesn&#8217;t really demonstrate the progress counter,\nso insert a <code>Sleep(1);<\/code> into the loop:\n<\/p>\n<pre>\nDWORD RootWindow::ThreadProc(void *p)\n{\n RootWindow*self = reinterpret_cast&lt;RootWindow*&gt;(p);\n for (int i = 0; i &lt; 100000; i++) {\n  self-&gt;m_value++;\n  InterlockedCompareExchangeRelease(&amp;m_fChanged, TRUE, FALSE);\n  <font COLOR=\"blue\">Sleep(1);<\/font>\n }\n MessageBeep(-1);\n return 0;\n}\n<\/pre>\n<p>\nThis slows down the loop enough that you can now see the values\nbeing incremented.\nIt&#8217;s not the dizzying incrementing that you saw in the original\nversion, but it&#8217;s fast enough that people will get the point.\n<\/p>\n<p>\nThe mechanism I used to pass information between the background and\nforeground thread assumed that background changes were comparatively\nfrequent, so that the timer will nearly always see something worth\ndoing.\nIf you have a mix of fast and slow tasks, you could change the\ncommunication mechanism so that the timer shut itself off when\nit noticed that some time has elapsed with no changes.\nThe background thread would then have to start the timer again\nwhen it resumed updating the value.\nI didn&#8217;t bother writing this more complicated version because it\nwould just be a distraction from the point of the article.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Sometimes, people discover that a long-running task runs faster if you hold down the mouse. How can that be? This strange state of affairs typically results when a program is spending too much time updating its progress status and not enough time actually doing work. (In other words, the programmer messed up badly.) When you [&hellip;]<\/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-32233","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Sometimes, people discover that a long-running task runs faster if you hold down the mouse. How can that be? This strange state of affairs typically results when a program is spending too much time updating its progress status and not enough time actually doing work. (In other words, the programmer messed up badly.) When you [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/32233","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=32233"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/32233\/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=32233"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=32233"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=32233"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}