copy arrays

Started by
35 comments, last by s_p_oneil 18 years, 10 months ago
On the contrary - learning when and what to optimize is a vital part of the formative period when learning to program. It's one of those good habits that must be developed now or will be rather hard to cultivate later (for most people).

The cases you provided aren't really good examples - because in your cases, the optimization was not premature. You yourself mentioned the use of a profiler, which indicates that you at least knew to look for a problem before just picking something and rewriting it because you were afraid of it being slow. Finding uniform inefficiency is not the same as premature optimization. Optimization is not bad - optimizing things that don't have any genuine effect on the overall performance is bad. As I've said, optimizing a data copy is rarely a serious performance concern, especially not with integral data types.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Advertisement
Quote:Original post by ApochPiQ
The cases you provided aren't really good examples - because in your cases, the optimization was not premature. You yourself mentioned the use of a profiler, which indicates that you at least knew to look for a problem before just picking something and rewriting it because you were afraid of it being slow. Finding uniform inefficiency is not the same as premature optimization. Optimization is not bad - optimizing things that don't have any genuine effect on the overall performance is bad. As I've said, optimizing a data copy is rarely a serious performance concern, especially not with integral data types.


Actually, I was arguing that waiting until we profiled it ended up being too late (or perhaps that we waited too long to start profiling and reviewing code). Some seem to be arguing that you shouldn't bother attempting to optimize code until it's feature-complete, and that doing so earlier is "the root of all evil". I suppose that is what I'm arguing against. So I guess I'm saying teach newbies how to use a profiler instead of discouraging exploration of optimization. ;-)

As a side note, sometimes you do need to optimize a data copy. I once wrote an assembler routine to replace memcpy. It used MMX to move 8 bytes at a time, and with the proper byte alignment and instruction pipelining, it was noticeably faster than memcpy. ;-) Of course, I didn't do that for the fun of it. It fit a specific need for a project I was working on, and I had similar routines for transforming the data while it was copied (all using MMX).
Quote:Original post by s_p_oneil
Quote:Original post by ApochPiQ
Generally speaking, when a programmer knows enough about when to optimize and clean up code, they don't need to ask for the fastest way to copy memory. In my experience, it is quite rare for optimizations to make code cleaner. Distinguish optimization and refactoring.

IMHO, a memcpy() call with explicit address taking and sizeof() juggling is messier than a simple loop. Short is not equivalent to clean.


I agree for the most part. However, if code is messy enough then there are usually noticeable inefficiencies that disappear when the code is refactored and cleaned up. This kind of optimization is not bad, even if the code doesn't contribute to any performance bottleneck.


I disagree (edit: misread you stated intent... ^_^;;), cleaned up code can easily preform as well as or better than messy, legacy spaghetti code. Case in point: string manipulation.

Q) Which do you think is faster:

1) std::string string3 = string1 + string2; //where string1 and 2 are existing std::strings

2) char * string3 = std::malloc( std::strlen( string1 ) + std::strlen( string2 ) + 1 ); //where string1 and string2 are existing (const char *)s.
std::strcpy( string3 , string1 );
std::strcat( string3 , string2 );

A) Most likely, 1, because std::string directly tracks the length of the string, thus avoiding needlessly counting through every byte of a string during every std::strlen and during every std::strcat (to find the end of the existing string) to find this number out. In other words, std::string avoids Shlemiel the painter's algorithm. See here for some details.

Similarly, using std::vector instead of std::realloc ing a buffer increasing it's size by 1 every time you add something, will likely result in a preformance gain, since std::vector doubles the size of memory allocated when overextended. This leads to O(N) preformance when inserting a sequence, whereas not doing so as with the common std::realloc leads to O(N*N) preformance.


Clean is important because it lends itself much better to algorithmic optimization than a pile of C voodoo tied together with bubblegum, if only because it's much easier to override one function instead of the entire C library.
Quote:Original post by s_p_oneil
3. Unless Christoph knows STL and the new C++ standard extensions, he should stick with the standard C version of memcpy until he's ready to jump into STL.


I'd argue that std::copy is much easier to learn than a memcpy. It works for everything, so you won't get "but why doesn't it work when I memcpy std::strings" and you wont get "why can't I just tell it to copy 3 things? Shouldn't it know how big they are?".

The other trick with a possible copy_n is what it would return--I think it might have to be a pair<in_iter,out_iter>. In any case, the count should definatly be second, since that follows the pattern of describing the input range, then specifying that start of the output.

Here's how I'd start out a copy_n:
template <typename in_iter, typename out_iter>std::pair<in_iter,out_iter> copy_n(in_iter b,                                   iterator_traits<in_iter>::difference_type n,                                   out_iter const out) {    while ( n-- ) *out++ = *b++;    return std::make_pair( b, out );}

Of course I'd also have it convert to a std::copy if they're random-access, and perhaps change the while loop to use preincrement and other stuff, but...

On the note of always implementing copy_n in terms of copy by using advance, there's always the issue of what that does if you write:
copy_n( istream_iterator<string>(cin), istream_iterator<string>(),
back_inserter( my_vec ) );

Quote:Original post by s_p_oneil
However, if code is messy enough then there are usually noticeable inefficiencies that disappear when the code is refactored and cleaned up. This kind of optimization is not bad, even if the code doesn't contribute to any performance bottleneck.


I don't think of that as optimisation whether it speeds it up or not. Sounds like "Refactor for Clarity" to me...

[Edited by - me22 on June 30, 2005 6:26:05 PM]
Quote:Original post by s_p_oneil
Actually, I was arguing that waiting until we profiled it ended up being too late (or perhaps that we waited too long to start profiling and reviewing code). Some seem to be arguing that you shouldn't bother attempting to optimize code until it's feature-complete, and that doing so earlier is "the root of all evil". I suppose that is what I'm arguing against. So I guess I'm saying teach newbies how to use a profiler instead of discouraging exploration of optimization. ;-)

As a side note, sometimes you do need to optimize a data copy. I once wrote an assembler routine to replace memcpy. It used MMX to move 8 bytes at a time, and with the proper byte alignment and instruction pipelining, it was noticeably faster than memcpy. ;-) Of course, I didn't do that for the fun of it. It fit a specific need for a project I was working on, and I had similar routines for transforming the data while it was copied (all using MMX).



As you say, the skill is in knowing when to start profiling. I definitely don't think that waiting until code is finished is the proper approach.

Of course there are cases where optimizing a data copy is important; I've been careful to word my posts in a way that leaves that possibility open [wink] But they are very rare. How many times have you genuinely needed that fast copy?


Clean code is of course often faster than messy code, but IMHO clean code should be pursued for the sake of clean code, not for the sake of performance. I don't mean to give the impression that doing anything to get a speed increase before Beta 1 is Evil - just that there is a time and place for doing deliberate optimization. I personally do not get the impression that the OP is at the right time or place, although that may be mistaken, as there isn't much detail offered.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

Quote:Original post by me22
Quote:Original post by s_p_oneil
3. Unless Christoph knows STL and the new C++ standard extensions, he should stick with the standard C version of memcpy until he's ready to jump into STL.


I'd argue that std::copy is much easier to learn than a memcpy. It works for everything, so you won't get "but why doesn't it work when I memcpy std::strings" and you wont get "why can't I just tell it to copy 3 things? Shouldn't it know how big they are?".


Agreed. I also disagree with the statement about sticking wit C versions.

C++ is not just C with a few features tacked on, and learning it that way is a bad idea. What was formerly known as the STL is now known as the Standard C++ Library, and it is important to use it, from the start if possible.

Case in point: I once did a brief stint at trying to teach a course in C++ for an elective at my high school. It failed miseribly, in part due to some retarded students which I hadn't the heart to leave in the dust, in part due to the fact that I have a tendancy to suck as a teacher I believe, and in part due to the fact that a sizeable portion of the class was still hung up on printf specifiers, and getting them wrong.

If I were to teach that class again, the first thing I would throw out would be printf. It's a horrible piece of junk. Instead of me spending 20 some odd days (it was a short elective) growing ulcers and repeating the few printfs specifiers we were using, I'd have taught how to use the iostream library.


I was glad when the class was over. One of the students felt it'd be funny to write his entry in my year book in C++. Basically, it was "Hello, world" modified to print "Have a good summer". No less than 3 bugs. I felt like banging my head on the table a few times.
Quote:How many times have you genuinely needed that fast copy?


Only once, of course. Plus, I found the ASM so unreadble after optimizing for the instruction pipelines that I had to keep two versions of it, one that was readable and one that was optimized. ;-)

Quote:Original post by MaulingMonkey
If I were to teach that class again, the first thing I would throw out would be printf. It's a horrible piece of junk. Instead of me spending 20 some odd days (it was a short elective) growing ulcers and repeating the few printfs specifiers we were using, I'd have taught how to use the iostream library.


Although I agree with the poor state of the old C/C++ runtime library, I think that aspiring programmers learning C/C++ should understand how the code works all the way down to the assembler level. Though I don't think they should have be able to write assembler code fluently, they should have a basic idea how the underlying machine instructions work and how the language's commands get turned into machine instructions, and how the stack differs from the heap. Otherwise, they should try an easier language first, like Java. All of the worst C++ developers I've worked with tried to learn the higher-level aspects of C++ without learning the foundations the language was built on. They had broken code any time they had to work with pointers, they had memory leaks and GPF's when using smart pointers without knowing why, and so on and so forth.

I had to beat that knowledge into one of the junior developers that was assigned to me. I made him write his own linked list implementation, then made him convert it to doubly linked, then made him derive queue and stack classes from each (to see the elegance and/or problems of each), and then I had him write a sorted binary tree with both recursive and non-recursive implementations. I helped him when he got stuck, made sure he understood them thoroughly (including pointers and dynamic allocation/deallocation), and then I had him templatize them. After that I briefly outlined a number of other algorithms to him just so he would know what was out there. Last but not least, I showed him how to use STL and told him never to use his own container class implementations until he had learned enough to be able to convince me why it was necessary. ;-) In a week or two he went from needing constant help and supervision to being one of the best junior developers in our development group. (The other thing I had to beat into him was that he should come to me right away when he got stuck on something instead of spending a day or two trying to come up with his own solution. He was very reluctant to do that until he realized that our boss was much more impressed with him getting things done more quickly than with him figuring things out on his own.)

EDIT: Actually, I made him write array-based stack and queue implementations before the linked lists.

This topic is closed to new replies.

Advertisement