{"id":35663,"date":"2005-05-11T08:56:46","date_gmt":"2005-05-11T08:56:46","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/2005\/05\/11\/loading-the-dictionary-part-2-character-conversion\/"},"modified":"2005-05-11T08:56:46","modified_gmt":"2005-05-11T08:56:46","slug":"loading-the-dictionary-part-2-character-conversion","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20050511-46\/?p=35663\/","title":{"rendered":"Loading the dictionary, part 2:  Character conversion"},"content":{"rendered":"<p>\nWhen you want to optimize a program, you first need to know\nwhere the time is being spent.\nThere&#8217;s no point optimizing a function that isn&#8217;t actually\nresponsible for your poor performance.\nFor example, if a particular function is responsible for 2%&nbsp;of\nyour CPU time, then even if you optimized it down to infinite speed,\nyour program would speed up at best by only little over&nbsp;2%.\nIn the comments to yesterday&#8217;s entry, several people put forth\nsuggestions as to how the program could be optimized,\nin the process quite amply demonstrating this principle.\nNone of the people who made suggestions actually investigated\nthe program to see where the glaring bottleneck was.\n<\/p>\n<p>\n(<a HREF=\"http:\/\/blogs.msdn.com\/ricom\">Rico Mariani<\/a> points out that you also\nneed to take performance in account when doing high level designs,\nchoosing algorithms and data structures that are suitable for\nthe level of performance you need.\nIf profiling reveals that a fundamental design decision\nis responsible for a performance bottleneck, you&#8217;re in big trouble.\nYou will see this sort of performance-guided design as the program develops.\nAnd you should check out\n<a HREF=\"http:\/\/blogs.msdn.com\/ricom\/archive\/2005\/05\/10\/416151.aspx\">\nPerformance Quiz&nbsp;#6<\/a>\nwhich starts with the very program we&#8217;re developing here.)\n<\/p>\n<p>\nUpon profiling our dictionary-loader, I discovered that\n80% of the CPU time was spent in <code>getline<\/code>.\nClearly this is where the focus needs to be.\nEverything else is just noise.\n<\/p>\n<p>\nDigging a little deeper, it turns out that\n29% of the CPU time was spent by <code>getline<\/code> doing\ncharacter set conversion in <code>codecvt::do_in<\/code>.\nSome debugging revealed that <code>codecvt::do_in<\/code>\nwas being called millions of times, each time converting\njust one or two characters.  In fact, for each character\nin the file, <code>codecvt::do_in<\/code> was called once\nand sometimes twice!\n<\/p>\n<p>\nLet&#8217;s get rid of the piecemeal character set conversion and\ninstead convert entire lines at a time.\n<\/p>\n<pre>\nDictionary::Dictionary()\n{\n std::<font COLOR=\"blue\">ifstream<\/font> src;\n <font COLOR=\"blue\">typedef std::codecvt&lt;wchar_t, char, mbstate_t&gt; widecvt;\n std::locale l(\".950\");\n const widecvt&amp; cvt = _USE(l, widecvt); \/\/ use_facet&lt;widecvt&gt;(l);<\/font>\n src.open(\"cedict.b5\");\n <font COLOR=\"blue\">string<\/font> s;\n while (getline(src, s)) {\n  if (s.length() &gt; 0 &amp;&amp; s[0] != L'#') {\n   <font COLOR=\"blue\">wchar_t* buf = new wchar_t[s.length()];\n   mbstate_t state = 0;\n   char* nextsrc;\n   wchar_t* nextto;\n   if (cvt.in(state, s.data(), s.data() + s.length(), nextsrc,\n                   buf, buf + s.length(), nextto) == widecvt::ok) {\n    wstring line(buf, nextto - buf);<\/font>\n    DictionaryEntry de;\n    if (de.Parse(<font COLOR=\"blue\">line<\/font>)) {\n     v.push_back(de);\n    }\n   <font COLOR=\"blue\">}\n   delete[] buf;<\/font>\n  }\n }\n}\n<\/pre>\n<p>\nInstead of using a <code>wifstream<\/code>,\nwe just use a non-Unicode <code>ifstream<\/code>\nand convert each line to Unicode manually.\nDoing it a line at a time rather than a character at a time,\nwe hope, will be more efficient.\n<\/p>\n<p>\nWe ask code page 950 for a converter, which we call <code>cvt<\/code>.\nNotice that the Microsoft&nbsp;C++ compiler requires you to use\nthe strange <code>_USE<\/code> macro instead of the more traditional\n<code>use_facet<\/code>.\n<\/p>\n<p>\nFor each line that isn&#8217;t a comment, we convert it to Unicode.\nOur lives are complicated by the fact that <code>codecvt::in<\/code>\nrequires pointers to elements rather than iterators, which means\nthat we can&#8217;t use a <code>wstring<\/code> or a <code>vector<\/code>;\nwe need a plain boring <code>wchar_t[]<\/code> array.\n(Notice that we can cheat on the &#8220;from&#8221; buffer and use the\n<code>string::data()<\/code> function to get at a read-only\narray representation of the string.)\nIf the conversion succeeds, we convert the array into a proper\nstring and continue as before.\n<\/p>\n<p>\nWith this tweak, the program now loads the dictionary in 1120ms\n(or 1180ms if you include the time it takes to destroy the\ndictionary).  That&#8217;s nearly twice as fast as the previous version.<\/p>\n<p>\nYou might think that we could avoid redundant allocations\nby caching the temporary conversion buffer between lines.\nI tried that, and surprisingly, it actually slowed the program\ndown by 10ms.\nSuch is\n<a HREF=\"http:\/\/blogs.msdn.com\/oldnewthing\/archive\/2004\/12\/16\/317157.aspx\">\nthe counter-intuitive world of optimization<\/a>.\nThat&#8217;s why it&#8217;s important to identify your bottlenecks via\nmeasurement instead of just guessing at them.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>When you want to optimize a program, you first need to know where the time is being spent. There&#8217;s no point optimizing a function that isn&#8217;t actually responsible for your poor performance. For example, if a particular function is responsible for 2%&nbsp;of your CPU time, then even if you optimized it down to infinite speed, [&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-35663","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>When you want to optimize a program, you first need to know where the time is being spent. There&#8217;s no point optimizing a function that isn&#8217;t actually responsible for your poor performance. For example, if a particular function is responsible for 2%&nbsp;of your CPU time, then even if you optimized it down to infinite speed, [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/35663","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=35663"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/35663\/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=35663"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=35663"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=35663"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}