copy constructors hurting performance

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Shea Martin

    copy constructors hurting performance

    I have a String class (I know I am re-inventing the wheel, yes I have
    heard of boost, and of QString).



    My copy constructor does a deep (strcpy) of the char *_buffer member.
    I have a member function func(const String &param). When an actual
    String is passed as the param this is nice and efficient.

    I have a constructor which takes a const char* as an argument. And
    performs a deep copy of the const char *buffer.

    The issue occurrs when I make the following call:
    func("this is the param");
    The String(const char *buf) constructor is called, making a deep copy of
    the data. Because func takes a const arg, it will never alter the
    buffer, thus the deep copy of the const char * is needless, and could be
    expensive.

    Does anyone see an easy and somewhat safe design, to avoid the needless
    copy's? Or is the only way to overload the methods to take a const
    char* as well?

    I think I already know the answer to this, but I would rather be safe
    that sorry.

    Thanks,

    ~S

  • Ron Natalie

    #2
    Re: copy constructors hurting performance


    "Shea Martin" <smartin@arcis. com> wrote in message news:bSQpb.1094 4$f7.584596@loc alhost...
    [color=blue]
    > The String(const char *buf) constructor is called, making a deep copy of
    > the data. Because func takes a const arg, it will never alter the
    > buffer, thus the deep copy of the const char * is needless, and could be
    > expensive.[/color]

    It may not be needless. What guarantees do you have that the char* passed
    to the constructor remains valid over the lifetime of the object. Imagine this:

    String* string_ptr;
    {
    char arg[] = "Transient string."
    string_ptr = new String(arg);
    }
    // at this point string_ptr if it just saves the pointer has a pointer to deallocated
    // memory.
    [color=blue]
    > Does anyone see an easy and somewhat safe design, to avoid the needless
    > copy's? Or is the only way to overload the methods to take a const
    > char* as well?[/color]

    Overloading has no bearing on the problem.

    Why are you reimplementing the wheel anyhow?


    Comment

    • Victor Bazarov

      #3
      Re: copy constructors hurting performance

      "Shea Martin" <smartin@arcis. com> wrote...[color=blue]
      > I have a String class (I know I am re-inventing the wheel, yes I have
      > heard of boost, and of QString).
      >
      >
      >
      > My copy constructor does a deep (strcpy) of the char *_buffer member.
      > I have a member function func(const String &param). When an actual
      > String is passed as the param this is nice and efficient.
      >
      > I have a constructor which takes a const char* as an argument. And
      > performs a deep copy of the const char *buffer.
      >
      > The issue occurrs when I make the following call:
      > func("this is the param");
      > The String(const char *buf) constructor is called, making a deep copy of
      > the data. Because func takes a const arg, it will never alter the
      > buffer, thus the deep copy of the const char * is needless, and could be
      > expensive.
      >
      > Does anyone see an easy and somewhat safe design, to avoid the needless
      > copy's? Or is the only way to overload the methods to take a const
      > char* as well?
      >
      > I think I already know the answer to this, but I would rather be safe
      > that sorry.[/color]

      IMHO, what you're looking for is a variation on reference-counting
      implementation. If you know that the string will never change, you
      could simply store the pointer instead of making a deep copy. I has
      to make a copy only when a mutating member function is called.

      It is, however, dangerous. The pointer you store may simply become
      invalid:

      char *ptr = new char[20];
      strcpy(ptr, "whatever") ;
      String str(ptr);
      delete[] ptr; // BOOM! the ptr is invalid, 'str' cannot use it
      // safely any longer.

      However, in the case of creating a temporary String from a string
      literal, it will work.

      Victor


      Comment

      • David Rubin

        #4
        Re: copy constructors hurting performance

        Shea Martin wrote:

        [snip - writing String class][color=blue]
        > My copy constructor does a deep (strcpy) of the char *_buffer member.
        > I have a member function func(const String &param). When an actual
        > String is passed as the param this is nice and efficient.
        >
        > I have a constructor which takes a const char* as an argument. And
        > performs a deep copy of the const char *buffer.
        >
        > The issue occurrs when I make the following call:
        > func("this is the param");
        > The String(const char *buf) constructor is called, making a deep copy of
        > the data. Because func takes a const arg, it will never alter the
        > buffer, thus the deep copy of the const char * is needless, and could be
        > expensive.[/color]

        I think you want to define

        void func(const char *buf);

        and overload for const String&:

        // pass the char* representation
        void func(const String& s) { func(s.rep); }

        This avoids calling the copy constructor. You can further protect
        against this unwanted conversion by making the String constructor
        'explicit':

        explicit String(const char* s);

        /david

        --
        "As a scientist, Throckmorton knew that if he were ever to break wind in
        the echo chamber, he would never hear the end of it."

        Comment

        • Shea Martin

          #5
          Re: copy constructors hurting performance

          Ron Natalie wrote:
          [color=blue]
          > It may not be needless. What guarantees do you have that the char* passed
          > to the constructor remains valid over the lifetime of the object. Imagine this:
          >
          > String* string_ptr;
          > {
          > char arg[] = "Transient string."
          > string_ptr = new String(arg);
          > }
          > // at this point string_ptr if it just saves the pointer has a pointer to deallocated
          > // memory.[/color]
          I am well aware of the this danger, that is why I do make a deep copy.
          That is why I posted here, to see if anyone knew of a different solution.
          [color=blue][color=green]
          >>Does anyone see an easy and somewhat safe design, to avoid the needless
          >>copy's? Or is the only way to overload the methods to take a const
          >>char* as well?[/color]
          >[/color]
          [color=blue]
          > Overloading has no bearing on the problem.[/color]
          Excuse me?
          int main()
          {
          func("print this please"); //String::String( const char *text) called
          String str("print this");
          func(str); //no constructor called, hence no mem alloc, and copy
          return 0;
          }

          void func(const String &text)
          {
          printf("String version\ntext is %s\n", text.cstr());
          }

          no if I add this function (btw this is called function overloading), I
          avoid the use of String::String( const char *text):

          String::String( const char *text)
          {
          internalBuffer = new char[strlen(text)+1];
          strcpy(internal Buffer, text);
          }
          [color=blue]
          > Why are you reimplementing the wheel anyhow?[/color]
          My managers are anti STL (and to be honest debugging code with STL in it
          is ugly, due to the templates, atleast w/ our debugger, dbx). The one
          does beleives that a class should never be used, but luckily the other
          does uses classes. My managers also have a phobia of 3rd party stuff,
          such as boost or QString. They are old-school. I have no control over
          this, and I like my paycheck, so I listen. I have a bunch of code I
          wrote using QString and std::string: they want me to remove all of the
          QString and STL stuff from it. Thus I would like a drop in replacement
          for the two. It is not like it is hard to make a String class. It only
          took me a day to may a replacement which covers all the functionality of
          std::string, and 75% of QString. I would just like to make it a little
          more efficient.

          ~S

          Comment

          • Shea Martin

            #6
            Re: copy constructors hurting performance

            Victor Bazarov wrote:[color=blue]
            > "Shea Martin" <smartin@arcis. com> wrote...
            >[color=green]
            >>I have a String class (I know I am re-inventing the wheel, yes I have
            >>heard of boost, and of QString).
            >>
            >>
            >>
            >>My copy constructor does a deep (strcpy) of the char *_buffer member.
            >>I have a member function func(const String &param). When an actual
            >>String is passed as the param this is nice and efficient.
            >>
            >>I have a constructor which takes a const char* as an argument. And
            >>performs a deep copy of the const char *buffer.
            >>
            >>The issue occurrs when I make the following call:
            >>func("this is the param");
            >>The String(const char *buf) constructor is called, making a deep copy of
            >>the data. Because func takes a const arg, it will never alter the
            >>buffer, thus the deep copy of the const char * is needless, and could be
            >>expensive.
            >>
            >>Does anyone see an easy and somewhat safe design, to avoid the needless
            >>copy's? Or is the only way to overload the methods to take a const
            >>char* as well?
            >>
            >>I think I already know the answer to this, but I would rather be safe
            >>that sorry.[/color]
            >
            >
            > IMHO, what you're looking for is a variation on reference-counting
            > implementation. If you know that the string will never change, you
            > could simply store the pointer instead of making a deep copy. I has
            > to make a copy only when a mutating member function is called.
            >
            > It is, however, dangerous. The pointer you store may simply become
            > invalid:
            >
            > char *ptr = new char[20];
            > strcpy(ptr, "whatever") ;
            > String str(ptr);
            > delete[] ptr; // BOOM! the ptr is invalid, 'str' cannot use it
            > // safely any longer.
            >
            > However, in the case of creating a temporary String from a string
            > literal, it will work.
            >
            > Victor
            >
            >[/color]
            I know. I was thinking that it would be nice to be able to overload a
            constructor based on whether it was a string literal or not... Or even
            better, remove char * from this universe, that would solve the issue
            too; a la Java. Unfortunately, have to remain compatible with rest of
            world.

            Thanks,

            ~S

            Comment

            • Shea Martin

              #7
              Re: copy constructors hurting performance

              David Rubin wrote:[color=blue]
              > Shea Martin wrote:
              >
              > [snip - writing String class]
              >[color=green]
              >> My copy constructor does a deep (strcpy) of the char *_buffer member.
              >> I have a member function func(const String &param). When an actual
              >> String is passed as the param this is nice and efficient.
              >>
              >> I have a constructor which takes a const char* as an argument. And
              >> performs a deep copy of the const char *buffer.
              >>
              >> The issue occurrs when I make the following call:
              >> func("this is the param");
              >> The String(const char *buf) constructor is called, making a deep copy
              >> of the data. Because func takes a const arg, it will never alter the
              >> buffer, thus the deep copy of the const char * is needless, and could
              >> be expensive.[/color]
              >
              >
              > I think you want to define
              >
              > void func(const char *buf);
              >
              > and overload for const String&:
              >
              > // pass the char* representation
              > void func(const String& s) { func(s.rep); }
              >
              > This avoids calling the copy constructor. You can further protect
              > against this unwanted conversion by making the String constructor
              > 'explicit':
              >
              > explicit String(const char* s);
              >
              > /david
              >[/color]
              That is what I thought, I was just being lazy, hoping for a dif sol'n.
              However, I am unfamiliar with the 'explicit' keyword. I am on my way
              over to the book shelf to look it up.

              Thanks,

              ~S

              Comment

              • Ron Natalie

                #8
                Re: copy constructors hurting performance


                "Shea Martin" <smartin@arcis. com> wrote in message news:2TRpb.1096 7$f7.585008@loc alhost...[color=blue]
                > Ron Natalie wrote:[color=green][color=darkred]
                > > > Overloading has no bearing on the problem.[/color][/color]
                > Excuse me?[/color]

                Just what I said. Overloading a const char* version of the constructor
                doesn't have any real outward effects (other than allowing you to initialize
                from const char*).

                [color=blue]
                > String::String( const char *text)
                > {
                > internalBuffer = new char[strlen(text)+1];
                > strcpy(internal Buffer, text);
                > }[/color]

                So why is this any different than the String(char*) case?
                [color=blue]
                > My managers are anti STL (and to be honest debugging code with STL in it
                > is ugly, due to the templates, atleast w/ our debugger, dbx).[/color]

                Your managers are ignorant fools. Get a better debugger or learn to deal
                with it. std::string is not STL, it's not some third-party add-on. It is an
                integral part of the standard C++ language.
                [color=blue]
                > The one
                > does beleives that a class should never be used, but luckily the other
                > does uses classes.[/color]

                As I said, they appear to be ignorant fools.
                [color=blue]
                > They are old-school. I have no control over
                > this, and I like my paycheck, so I listen. I have a bunch of code I
                > wrote using QString and std::string: they want me to remove all of the
                > QString and STL stuff from it. Thus I would like a drop in replacement
                > for the two. It is not like it is hard to make a String class. It only
                > took me a day to may a replacement which covers all the functionality of
                > std::string, and 75% of QString. I would just like to make it a little
                > more efficient.[/color]

                Great, and now you must maintain it, and I warrant you haven't discovered
                all the problems with it, and six months from now when they want you to use
                wchar_t because you've added unicode you'll have to recode it.


                Comment

                • Ron Natalie

                  #9
                  Re: copy constructors hurting performance


                  "Shea Martin" <smartin@arcis. com> wrote in message news:oWRpb.1096 8$f7.584885@loc alhost...
                  [color=blue][color=green]
                  > >[/color]
                  > I know. I was thinking that it would be nice to be able to overload a
                  > constructor based on whether it was a string literal or not... Or even
                  > better, remove char * from this universe, that would solve the issue
                  > too; a la Java. Unfortunately, have to remain compatible with rest of
                  > world.
                  >[/color]
                  const char* doesn't tell you if it is a string literal or not. You can have
                  const char* values that aren't string literals.


                  Comment

                  • Cagdas Ozgenc

                    #10
                    Re: copy constructors hurting performance

                    [color=blue]
                    > It is, however, dangerous. The pointer you store may simply become
                    > invalid:
                    >
                    > char *ptr = new char[20];
                    > strcpy(ptr, "whatever") ;
                    > String str(ptr);
                    > delete[] ptr; // BOOM! the ptr is invalid, 'str' cannot use it
                    > // safely any longer.
                    >
                    > However, in the case of creating a temporary String from a string
                    > literal, it will work.[/color]

                    I think this is not a good illustration, how about

                    in some header file:

                    typedef char *String;

                    in implementation file:

                    char *ptr = new char[20];
                    strcpy(ptr,"hel lo");

                    String str = ptr; // illusion of copy constructor
                    delete[] ptr; // BOOM

                    As you can see this is problematic only when the deletion is in another
                    scope. Within the same scope it is programmer's responsibility not to hack
                    the branch he is sitting on.



                    Comment

                    • Cagdas Ozgenc

                      #11
                      Re: copy constructors hurting performance

                      In contrast to what others said, I have a feeling that what you dream of may
                      be achieved with delicacy, but I would never do it myself.

                      First of all, you need to be able to detect whether an object is sitting on
                      the stack or free store:

                      class String {
                      public:
                      String(char const * const arg) {
                      Do some O/S compiler specific stuff and determine whether arg and
                      this pointer points to stack or free store
                      If arg is on free store do a deep copy. If arg is on stack, do NOT
                      do deep copy if this object is on stack otherwise do deep copy.
                      }

                      String(String const& arg) {
                      Do some O/S compiler specific stuff and determine whether &arg and
                      this pointer points to stack or free store
                      If arg is on free store do a deep copy. If arg is on stack, do NOT
                      do deep copy if this object is on stack otherwise do deep copy.
                      }
                      };


                      I suppose according to the scope rules a programmer should respect, the
                      above, not carefully thought, highly dangerous implementation might do the
                      trick. It's just an idea...


                      Comment

                      • Ron Natalie

                        #12
                        Re: copy constructors hurting performance


                        "Cagdas Ozgenc" <co19@XinvalidX .cornell.edu> wrote in message news:bo8vor$mq$ 1@news01.cit.co rnell.edu...
                        [color=blue]
                        > Do some O/S compiler specific stuff and determine whether arg and
                        > this pointer points to stack or free store
                        >[/color]
                        There's also statically allocated objects in addition to stack (auto) and free store.

                        But none of this helps either. What happens if someone changes characters
                        referred to by the passed in pointer and you haven't done the deep copy.

                        char foo[] = "yada yada"

                        String foostring(foo);

                        strcpy(foo, "daba daba");

                        Now what do you expect the value of foostring to be?


                        Comment

                        • Shea Martin

                          #13
                          Re: copy constructors hurting performance

                          Ron Natalie wrote:[color=blue]
                          > Just what I said. Overloading a const char* version of the constructor
                          > doesn't have any real outward effects (other than allowing you to initialize
                          > from const char*).[/color]
                          I meant overload the functions which take a const String reference, and
                          man make a version that takes a const char* as well. Then when these
                          methods are called, no constructor is called.[color=blue]
                          >
                          >
                          >[color=green]
                          >>String::Strin g(const char *text)
                          >>{
                          >> internalBuffer = new char[strlen(text)+1];
                          >> strcpy(internal Buffer, text);
                          >>}[/color]
                          >
                          >
                          > So why is this any different than the String(char*) case?
                          >
                          >[color=green]
                          >>My managers are anti STL (and to be honest debugging code with STL in it
                          >>is ugly, due to the templates, atleast w/ our debugger, dbx).[/color]
                          >
                          >
                          > Your managers are ignorant fools. Get a better debugger or learn to deal
                          > with it. std::string is not STL, it's not some third-party add-on. It is an
                          > integral part of the standard C++ language.
                          >[color=green]
                          >>The one
                          >>does beleives that a class should never be used, but luckily the other
                          >>does uses classes.[/color]
                          >
                          >
                          > As I said, they appear to be ignorant fools.
                          >
                          >[color=green]
                          >>They are old-school. I have no control over
                          >>this, and I like my paycheck, so I listen. I have a bunch of code I
                          >>wrote using QString and std::string: they want me to remove all of the
                          >>QString and STL stuff from it. Thus I would like a drop in replacement
                          >>for the two. It is not like it is hard to make a String class. It only
                          >>took me a day to may a replacement which covers all the functionality of
                          >>std::string , and 75% of QString. I would just like to make it a little
                          >>more efficient.[/color]
                          >
                          >
                          > Great, and now you must maintain it, and I warrant you haven't discovered
                          > all the problems with it, and six months from now when they want you to use
                          > wchar_t because you've added unicode you'll have to recode it.
                          >[/color]
                          I don't think we will need unicode anytime soon, but point taken. I am
                          well aware of the downfall of reinventing, but am given little alternative.

                          What is your suggestion? March into their office and state "According
                          to Ron Natalie, you are ignorant fools. From this day forth, I shall be
                          using std::string. So take your char pointers and stuff up your royal
                          arses! I'll be in my office if you need me."

                          Basically I have 2 options, roll my own, or use char*. Maybe I am
                          oversciencing this, maybe I should just use char*? I have chewed on
                          this decision for a while.

                          It is easy to be a critic, if you don't have to have a solution.

                          ~S

                          Comment

                          • Shea Martin

                            #14
                            Re: copy constructors hurting performance

                            [color=blue]
                            > As I said, they appear to be ignorant fools.[/color]
                            It gets worse:
                            Sun Compilers generate a disk cache when compiling STL code. My
                            managers had never seen it before I was hired. They panicked. They
                            want all code, that causes the SunWS_cache directory to be created,
                            removed. This includes my maps, list, cout, cerr, cin, ofstream,
                            ifstream**, etc. Whoa, is the life of a junior programmer.

                            ~S

                            **: turns out that most of the fstream code has already been removed
                            anyway, due to the fact that we frequently deal with files larger than
                            2GB, which is a fstream limit. This limit is bypassed on Solaris by
                            compiling with -xarch=v9, but makes binaries which are incompatible with
                            32 bit libs (which we have to use.)

                            Comment

                            • Ron Natalie

                              #15
                              Re: copy constructors hurting performance


                              "Shea Martin" <smartin@arcis. com> wrote in message news:%ITpb.1099 5$f7.586525@loc alhost...
                              [color=blue]
                              > Basically I have 2 options, roll my own, or use char*. Maybe I am
                              > oversciencing this, maybe I should just use char*? I have chewed on
                              > this decision for a while.[/color]

                              Why don't you "roll your own" by taking an open source implementation of
                              std::string and using that? Feel free to change the leading s in string to
                              upper case.


                              Comment

                              Working...