|
|
|
|||||
|
Documentation bug (C++ argument calculation order && exception safety)
Hi.
I found a really strange thing in documentation: Quote:
1. new PhoneEntryI(name, phNum); 2. comm->stringToIdentity(name)); 3. ObjectPtr(PhoneEntryI *) if step (2) generates exception (i look at stringToIdentity code and see that this is really possible), it causes a memory leak. Please, fix it, at least, in documentation ![]() |
|
||||||
|
Hi Andrew,
I think that (unfortunately), you are right, this could indeed happen I'll fix the documentation. Sadly, the undefined order of evaluation of function arguments is a constant source of subtle and hideous bugs. And, as far as I can see, defining the order of evaluation as left-to-right would not close any significant optimization opportunities, but would make life for programmers a lot easier. Quite a lot of people have complained about this many times in the past. See this comp.lang.c++.moderated post for one example. Thanks for pointing out this bug! Cheers, Michi. |
|
|||||
|
This is OT but as Hyman himself points out, there _are_ cases where it isn't so easy to determine which way the order of evaluation must proceed:
Order of evaluation in new expression - comp.lang.c++.moderated | Google Groups I guess this is one of those endless C++ quirks that will never die... |
|
|||||
|
Quote:
I think, that this feature can really help compiler. Keep in mind, that with intensive inline expansion you can really achieve great perfomance benefit from instruction mixing (f.e., instruction pairing on modern CPU's). So, i think, this is really important feature. BTW, about performance.. We had compared proxy invocation latency with ICE and OmniOrb. In most cases, OmniOrb beats ICE for about 3 times And, more over, ICE generates slightly more traffic, than orb... So, i think, you should think about optimizations - surely, proxy invocation can be much faster ![]() |
|
|||||
|
Quote:
Yes, I understand, that support for ami etc should slow down invocation in some cases. But, surely, threading model (based on select) is not best solution on the Windows, and, i sure again, you know it ![]() And I keep in mind, that twoway empty invocation should have comparable performance in all brokers, so I think, that other (first, I think, "thick" proxy itself) bottle neck in this pattern is an effective working with network layer: call packing into protocol and effective protocol itself. But proxy... you can easily compare quality of a proxy realisation with a simple test: run client and server parts of the latency test on the same host. You should see great difference in CPU load/per call... Quote:
Quote:
thread pool performance (500+ clients) Second test was a latency of an empty/one generic parameter twoway call from a server to a number of a clients. When only one client connected results identical with the ICE latency test. If you want exact numbers, i can post it, but with throughput pattern ICE provides for about 5-6% greater network load, than corba or wcf. We did not measure traffic in a latency tests/empty calls, but I think, that difference will be at least the same. In latency test ominORB outperforms ICE for about 3 times, and have much better latency degrade trend with higher count of the simultaneous connections. |
|
||||||
|
Quote:
The reason for the undefined evaluation order is simply historical and dates back to the dim distant past of C (thirtyfive years now), and there was even less reason for undefined evaluation order back then than there is now. Historically, the undefined order did not come about to preserve optimization opportunities (that was a justification after the fact). Instead, original C never specified it and, by the time the standard was written, different compilers did it in different orders. Seeing that the job of a standard is to codify existing practice, the standard did not mandate an evaluation order. There is no shortage of experts who agree the undefined order is harmful. Here is what Bjarne has to say about it: The value of j is unspecified to allow compilers to produce optimal code. It is claimed that the difference between what can be produced giving the compiler this freedom and requiring "ordinary left-to-right evaluation" can be significant. I'm unconvinced, but with innumerable compilers "out there" taking advantage of the freedom and some people passionately defending that freedom, a change would be difficult and could take decades to penetrate to the distant corners of the C and C++ worlds. Cheers, Michi. Last edited by michi : 05-07-2008 at 10:18 PM. |
|
|||||
|
Quote:
More over, it is main paradigm of C++ - 'do not pay for what you do not use'. And when i see generated with global optimizations code, I understand, that this freedom in C++ is really necessary. Thats my opinion, but it does not apply to my initial message - bugs, surely, are independed from our opinion ![]() |
|
||||||
|
Quote:
Consider the following: Code:
void foo(const TPtr&); Code:
foo(new T); Now consider: Code:
void foo(const TPtr&, int); Code:
foo(new T, 99); // No problem Code:
foo(new T, bar()); // Might leak memory For one, it is completely counter-intuitive that the following two code fragments do not do the same thing: Code:
TPtr t = new T; foo(t, bar()); Code:
foo(new T, bar()); But the real issue is the hidden maintenance problems. Consider the following code, which may have been working flawlessly for many years: Code:
foo(new T, bar()); And we haven't even considered the difficulty of making bar throw during testing yet (which can be very difficult for some functions). But, it's not just the implementation of bar that can cause this problem. Instead, it can be caused by any function that bar calls recursively. In other words, if I add a throw statement to the implementation of bar or any of the functions called by bar, I have to examine the entire call graph to see whether there might be a memory allocation and a call to bar (or any of the functions called by bar that now can throw an exception) without an intervening sequence point. Doing this is next to impossible for large projects. And we haven't even considered yet that bar, or one of the functions called by bar, might be implemented in a third-party library that can change in arbitrary ways. Even a statement such as the following is not entirely safe: Code:
foo(new T); Code:
void foo(auto_ptr<T>, int = bar()); Searching the web for relevant articles reveals that this issue (and the related issue of undefined evaluation of expressions that do not contain a sequence point) has bitten countless thousands of programmers time and time again. I wrote the broken code you pointed out myself, and I have been programming in C++ for twenty years now. I'm not a guru, but I consider myself quite experienced. Yet, I still wrote the bug (despite having known about the undefined evaluation order for more than two decades). If it is that easy to write broken code, the problem is with the language, in my opinion. (In particular because the actual bug is highly unlikely to show up, except in rare and exotic circumstances.) The evaluation order is a carefully hidden trap that the language puts in the programmer's path so he can impale himself on the poisened spikes at the bottom... Quote:
Quote:
And thanks again for reporting the bug! Cheers, Michi. |
|
|||||
|
Quote:
For example, this simple code can cause very strange behaviours: Code:
#include <string>
std::string generate_part_one(); // can cause exception
std::string generate_part_two();
int main()
{
buffered_file f ("1.txt"); // buffers some data internally, dtor flushes all buffered data
f << generate_part_one();
f << generate_part_two();
}
![]() Btw, are there any plans to optimize ice thread pool under windows? IOCP etc? |
|
||||||
|
Quote:
Care to enlighten me why the destructor may not run?Quote:
Cheers, Michi. |
|
||||||
|
Hi Andrew,
Quote:
Quote:
Quote:
In any case, I'd be happy to look at the numbers. We compared some time ago performances of Ice, Ice-E and omniORB. At the time, Ice-E latency performed as well as omniORB and Ice latency was about 1.2 times slower. There's one test where omniORB performed better by a larger factor: the sending of large byte sequences. omniORB implements zero-copy on the client and server side where Ice/Ice-E only implements it on the server side. In this test, ominORB was up to 2 times faster than Ice. So I'm still surprised that Ice performed 3 times slower for you... It would be great if you could detail some more your tests. The best would be to post a small self-compilable test case that demonstrates this (I would open another thread as this doesn't really have much to do anymore with the original subject ).Cheers, Benoit. |
|
|||||
|
Quote:
from C++ ISO/1998:Quote:
Code:
int main()
{
try
{
// impl
}
catch (...)
{
throw;
}
}
|
|
|||||
|
Quote:
|