⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wwwtc2answer.htm

📁 C++builder学习资料C++builder
💻 HTM
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<HTML><HEAD><TITLE>answer What's wrong with this code? Volume #2</TITLE>

<META content="text/html; charset=gb2312" http-equiv=Content-Type>

<META content="Harold Howe" name=Author>

<META content="MSHTML 5.00.2614.3500" name=GENERATOR></HEAD>

<BODY>

<CENTER>

<TABLE border=0 cellPadding=0 cellSpacing=0 width=640>

  <TBODY>

  <TR>

    <TD>

      <H2>Answer What's wrong with this code? Volume #2 </H2>

      <H4>A benchmark gone bad </H4>

      <P>Before we begin, let's post the code for the <TT>ClassNameIs</TT> 

      routine again. </P><PRE><FONT color=navy>// Benchmark for ClassNameIs</FONT>

<B>void</B> <B>__fastcall</B> TForm1<B>:</B><B>:</B>Button2Click<B>(</B>TObject <B>*</B>Sender<B>)</B>

<B>{</B>

    <B>int</B> nCount <B>=</B> ComponentCount<B>;</B>



    DWORD dwStart <B>=</B> GetTickCount<B>(</B><B>)</B><B>;</B>

    <B>for</B> <B>(</B><B>int</B> j<B>=</B><FONT color=blue>0</FONT><B>;</B> j&lt;<FONT color=blue>1000000</FONT><B>;</B> j<B>++</B><B>)</B>

    <B>{</B>

        <B>for</B> <B>(</B><B>int</B> i<B>=</B><FONT color=blue>0</FONT><B>;</B> i&lt;nCount<B>;</B> i<B>++</B><B>)</B>

        <B>{</B>

            <B>if</B><B>(</B>Components<B>[</B>i<B>]</B><B>-&gt;</B>ClassNameIs<B>(</B><FONT color=blue>"TEdit"</FONT><B>)</B><B>)</B>

                <B>(</B><B>(</B>TEdit <B>*</B><B>)</B>Components<B>[</B>i<B>]</B><B>)</B><B>-&gt;</B>Color <B>=</B> clBtnFace<B>;</B>

        <B>}</B>

     <B>}</B>

    DWORD dwEnd <B>=</B> GetTickCount<B>(</B><B>)</B><B>;</B>



    DWORD dwDiff <B>=</B> dwEnd <B>-</B> dwStart<B>;</B>

    Label2<B>-&gt;</B>Caption <B>=</B> IntToStr<B>(</B>dwDiff<B>)</B><B>;</B>

<B>}</B>

</PRE>

      <P>The flaw in this benchmark is the way that we call the 

      <TT>ClassNameIs</TT> function. <TT>ClassNameIs</TT> takes an 

      <TT>AnsiString</TT> object by value as its only argument. The benchmark 

      code passes <TT>char *</TT>. The compiler inserts code to construct a 

      temporary <TT>AnsiString</TT> object from the <TT>char *</TT>. The 

      temporary is constructed by using the <TT>char *</TT> conversion 

      constructor of <TT>AnsiString</TT>. </P><PRE><FONT color=navy>// inside class declaration for AnsiString</FONT>

<B>__fastcall</B> AnsiString<B>(</B><B>const</B> <B>char</B><B>*</B> src<B>)</B><B>;</B>

</PRE>

      <P>This constructor works by allocating a buffer large enough to hold the 

      string. It then calls the equivalent of <TT>strcpy</TT> to copy the string 

      from the source. If the code for this constructor was written in C++, it 

      might look something like this: <PRE><B>__fastcall</B> AnsiString<B>:</B><B>:</B>AnsiString<B>(</B><B>const</B> <B>char</B><B>*</B> str<B>)</B>



<B>{</B>

    Data <B>=</B> <B>new</B> <B>char</B><B>[</B>strlen<B>(</B><B>char</B><B>)</B> <B>+</B> <FONT color=blue>1</FONT><B>]</B><B>;</B>

    strcpy<B>(</B>Data<B>,</B>str<B>)</B><B>;</B>

    refcnt <B>=</B> <FONT color=blue>1</FONT><B>;</B>

<B>}</B>

</PRE>

      <P>Every time we pass a <TT>char *</TT> to <TT>ClassNameIs</TT>, this 

      constructor runs. Since we call <TT>ClassNameIs</TT> 16 million times in 

      the benchmark, we also call <TT>new</TT>, <TT>strcpy</TT>, and 

      <TT>strlen</TT> 16 million times. The overhead of this constructor affects 

      the benchmark. We are not just benchmarking <TT>ClassNameIs</TT>, we are 

      also benchmarking the string manipulation and the memory allocation 

      routines. </P>

      <P>If we eliminate the overhead of the memory allocation and string 

      routines, we can obtain a more accurate benchmark for 

      <TT>ClassNameIs</TT>. We accomplish this by creating an 

      <TT>AnsiString</TT> constant and passing the contant to 

      <TT>ClassNameIs</TT>. The new code lookes like this. </P><PRE><B>void</B> <B>__fastcall</B> TForm1<B>:</B><B>:</B>Button2Click<B>(</B>TObject <B>*</B>Sender<B>)</B>

<B>{</B>

    <B>const</B> AnsiString strEdit<B>(</B><FONT color=blue>"TEdit"</FONT><B>)</B><B>;</B>

    <B>int</B> nCount <B>=</B> ComponentCount<B>;</B>



    DWORD dwStart <B>=</B> GetTickCount<B>(</B><B>)</B><B>;</B>

    <B>for</B> <B>(</B><B>int</B> j<B>=</B><FONT color=blue>0</FONT><B>;</B> j&lt;<FONT color=blue>1000000</FONT><B>;</B> j<B>++</B><B>)</B>

    <B>{</B>

        <B>for</B> <B>(</B><B>int</B> i<B>=</B><FONT color=blue>0</FONT><B>;</B> i&lt;nCount<B>;</B> i<B>++</B><B>)</B>

        <B>{</B>

            <B>if</B><B>(</B>Components<B>[</B>i<B>]</B><B>-&gt;</B>ClassNameIs<B>(</B>strEdit<B>)</B><B>)</B>

                <B>(</B><B>(</B>TEdit <B>*</B><B>)</B>Components<B>[</B>i<B>]</B><B>)</B><B>-&gt;</B>Color <B>=</B> clBtnFace<B>;</B>

        <B>}</B>

    <B>}</B>

    DWORD dwEnd <B>=</B> GetTickCount<B>(</B><B>)</B><B>;</B>



    DWORD dwDiff <B>=</B> dwEnd <B>-</B> dwStart<B>;</B>

    Label2<B>-&gt;</B>Caption <B>=</B> IntToStr<B>(</B>dwDiff<B>)</B><B>;</B>

<B>}</B>

</PRE>

      <P>So what does this accomplish? Instead of calling the conversion 

      constructor for <TT>char *</TT>, the improved code calls the copy 

      constructor to initialize the temporary <TT>AnsiString</TT> that is passed 

      to <TT>ClassNameIs</TT>. Is the copy constructor any better than the 

      conversion constructor for <TT>char *</TT>? As it turns out, yes, it is 

      much better. Remember that the <TT>AnsiString</TT> class employs a 

      reference counting scheme. When you create an <TT>AnsiString</TT> object 

      using the copy constructor, the VCL simply copies the internal 

      <TT>Data</TT> pointer of the source object, and increments its reference 

      count. This is much faster than allocating new memory and copying the 

      string. </P>

      <P>The <TT>AnsiString</TT> copy constructor works effectively like this: 

      </P><PRE><B>__fastcall</B> AnsiString<B>:</B><B>:</B>AnsiString<B>(</B><B>const</B> AnsiString <B>&amp;</B>rhs<B>)</B>

<B>{</B>

    Data <B>=</B> rhs<B>.</B>Data<B>;</B>

    refcnt <B>=</B> <B>++</B>rhs<B>.</B>refcnt<B>;</B>

<B>}</B>

</PRE>

      <P>This constructor is much faster than the conversion constructor. When 

      we use this constructor, we get a more accurate portrayal of how well 

      <TT>ClassNameIs</TT> runs. After making this change, the result for 

      <TT>ClassNameIs</TT> dropped from 67,000 milli-seconds to 29,000 

      milli-seconds. Using the <TT>AnsiString</TT> constant cut the time by more 

      than half. </P>

      <P>So now we have to ask the question, which benchmark accurately reflects 

      the perforamance of <TT>ClassNameIs</TT>? If you are going to pass 

      <TT>char *</TT> arguments to <TT>ClassNameIs</TT>, then the first 

      benchmark is better because it includes the overhead of creating the 

      <TT>AnsiString</TT> temporary using the conversion constructor. This 

      overhead is a real factor when you pass <TT>char *</TT> arguments to 

      <TT>ClassNameIs</TT>. Another thing to keep in mind is that creating the 

      <TT>AnsiString</TT> constant requires some overhead too, because the 

      constant is created with the <TT>char *</TT> conversion constructor. If 

      you only need to call <TT>ClassNameIs</TT> one time, then there is no 

      point messing with a constant. If you do need to call <TT>ClassNameIs</TT> 

      dozens of times, and you are going to be passing the same class name each 

      time, then it makes sense to create a constant instead of passing a 

      <TT>char *</TT>. </P>

      <P>In reality, you probably won't need to call <TT>ClassNameIs</TT> very 

      much anyway since <TT>dynamic_cast</TT> and <TT>__classid</TT> beat it so 

      convincingly. There really isn't much point using <TT>ClassNameIs</TT> 

      when the other two options work so much more efficiently. Nonetheless, the 

      lesson from this benchmark is one worth remembering. Be aware of temporary 

      objects and how much processor time is spent creating and destroying them. 

      </P>

      <P><B>Note:</B> The <TT>AnsiString</TT> constructor code that is listed in 

      this edition is C++ pseudo-code. The <TT>AnsiString</TT> class does not 

      use <TT>new</TT>, <TT>strlen</TT>, and <TT>strcpy</TT>. It uses the pascal 

      equivalents. Also, <TT>refcnt</TT> is not a member variable of the class. 

      The reference count is maintained by pascal code in <TT>SYSTEM.PAS</TT>. 

      </P>

      <P><B>Note:</B> The <TT>ClassNameIs</TT> function takes a <TT>const 

      AnsiString</TT> object by value. The function would be more effecient if 

      it took a reference instead of an object by value. I would guess that 

      passing the object by value has something to do with the fact that the 

      underlying code is in pascal. 

</P></TD></TR></TBODY></TABLE></CENTER></BODY></HTML>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -