Skip to content

Switch to copy-and-swap idiom for operator=. #37

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 9, 2014
Merged

Switch to copy-and-swap idiom for operator=. #37

merged 1 commit into from
Sep 9, 2014

Conversation

BillyDonahue
Copy link
Contributor

This allows the compiler to elide a copy when rhs is a temporary.

This allows the compiler to elide a copy when rhs is a temporary.
@BillyDonahue
Copy link
Contributor Author

Common patterns like: json_obj[key] = strval; save a strdup from this optimization.
It's not an API change, it just moves the copy-construction of 'tmp' from the interior of the operator=, to its parameter list, where the compiler can do it for us more efficiently.

@jacobsa
Copy link
Contributor

jacobsa commented Sep 9, 2014

For the benefit of anybody following along at home, the performance effect here
is the thesis of the article "Want speed? Pass by value" by Dave
Abrahams. Unfortunately that link is currently broken, but it is summarized on
Stack Overflow here.

jacobsa added a commit that referenced this pull request Sep 9, 2014
Switch to copy-and-swap idiom for operator=.
@jacobsa jacobsa merged commit 1ce1ea1 into open-source-parsers:master Sep 9, 2014
@Xiao-Chong
Copy link

Hello, thanks for this useful project. However, I don't think it's beneficial to merge the code like that. IMHO, someone on stackoverflow said is true ONLY when compiler implicitly create move ctors for class. But since your classes had manually implemented its ctors and/or dtor, compiler would not create move ctors for them. Please kindly refer to flowing documentation:
http://en.cppreference.com/w/cpp/language/move_constructor
If you can add move ctor manually or ���force a move ctor, it might be fine.
If Im wrong, please kindly correct me. Thank you.

@BillyDonahue BillyDonahue deleted the value-efficiency branch September 9, 2014 16:44
@BillyDonahue
Copy link
Contributor Author

Xiao-Chong, the benefits of copy-and-swap are clear here. Do you have a link to the StackOverflow reference you're citing? The copy was being made anyway inside the body of the move constructor, so this isn't ever slower than the old code. When the rhs of the assignment is a temporary, the compiler can apply "copy elision" to save that copy from happening, and we win. You are right that we can do better still than this by adding rvalue-reference overloads of operator= and the ctors, but I was reluctant to introduce C++11 into the project for my first patch :). I can certainly continue to make improvements like that if they'd be welcomed, braced by "#if __cplusplus>=201103L" version checks, of course.

@Xiao-Chong
Copy link

@BillyDonahue , Thanks for your reply and explanation. I didn't notice that "copy elision" benefits. ;-)
Good job!

jacobsa added a commit that referenced this pull request Sep 10, 2014
Switch to copy-and-swap idiom for operator=.
@cdunn2001
Copy link
Contributor

Copy elision is a Good Thing. However, operator=() was inlined, so I suspect that the compiler could make the optimization anyway. Did anyone check the assembly? Just curious.... Oh, wait. It was inlined only internally. I wonder why.

Yes, C++11 changes, with #if, is a welcome idea.

I have rebased this change to simplify the history.

@BillyDonahue
Copy link
Contributor Author

I didn't check the assembly, but there are only specific contexts in which the compiler is allowed to elide a copy, even if it has side effects. This optimization opportunity for copies is more aggressive than what's allowed for an inlining optimizer, which can't ever choose to omit side effects.

Relevant std section here:
https://isocpp.org/files/papers/N3797.pdf [class.copy],paragraph31:

"When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization"

Passing a temporary to a function argument is one case, returning a local from a function is another. But copies like the one in the body of the old operator= are not elidable. The optimizer can't know that the copy+swap+destruct will produce the same effect as eliding the copy. There are malloc and free calls in the copy ctor and destructor, so there are nonlocal side effects. But even if that weren't the case, I'd be shocked to see the whole sequence omitted by an inlining optimizer.

Glad to see you're open to C++11 optimizations!

@cdunn2001 cdunn2001 mentioned this pull request Sep 11, 2014
@cdunn2001
Copy link
Contributor

Glad to see you're open to C++11 optimizations!

See issue #40

@cdunn2001 cdunn2001 mentioned this pull request Jan 1, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants