Jump to page: 1 222  
Page
Thread overview
assert semantic change proposal
Aug 03, 2014
David Bregman
Aug 03, 2014
bachmeier
Aug 03, 2014
David Bregman
Aug 03, 2014
Daniel Gibson
Aug 03, 2014
John Carter
Aug 03, 2014
David Bregman
Aug 03, 2014
John Carter
Aug 03, 2014
John Carter
Aug 03, 2014
David Bregman
Aug 04, 2014
David Bregman
Aug 04, 2014
David Bregman
Aug 04, 2014
David Bregman
Aug 05, 2014
Kagamin
Aug 03, 2014
Daniel Gibson
Aug 03, 2014
Dmitry Olshansky
Aug 03, 2014
Daniel Gibson
Aug 04, 2014
Daniel Gibson
Aug 04, 2014
Daniel Gibson
Aug 03, 2014
Timon Gehr
Aug 04, 2014
Daniel Gibson
Aug 06, 2014
Walter Bright
Aug 03, 2014
David Bregman
Aug 03, 2014
John Carter
Aug 03, 2014
Timon Gehr
Aug 03, 2014
David Bregman
Aug 04, 2014
deadalnix
Aug 04, 2014
David Bregman
Aug 04, 2014
deadalnix
Aug 03, 2014
Martin Krejcirik
Aug 03, 2014
David Bregman
Aug 06, 2014
Walter Bright
Aug 06, 2014
Kagamin
Aug 03, 2014
Mike Farnsworth
Aug 04, 2014
David Bregman
Aug 04, 2014
Daniel Gibson
Aug 04, 2014
Mike Farnsworth
Aug 06, 2014
Walter Bright
Aug 06, 2014
Tofu Ninja
Aug 06, 2014
eles
Aug 06, 2014
Walter Bright
Aug 06, 2014
Tofu Ninja
Aug 06, 2014
David Bregman
Aug 06, 2014
Kagamin
Aug 04, 2014
John Carter
Aug 04, 2014
David Bregman
Aug 04, 2014
John Carter
Aug 04, 2014
John Carter
Aug 04, 2014
David Bregman
Aug 06, 2014
Walter Bright
Aug 04, 2014
Daniel Gibson
Aug 04, 2014
Tove
Aug 06, 2014
Walter Bright
Aug 06, 2014
Tove
Aug 09, 2014
Sebastiaan Koppe
Aug 04, 2014
Atila Neves
Aug 04, 2014
David Bregman
Aug 04, 2014
Marc Schütz
Aug 04, 2014
Matthias Bentrup
Aug 05, 2014
Walter Bright
Aug 05, 2014
Jeremy Powers
Aug 05, 2014
Timon Gehr
Aug 05, 2014
Jeremy Powers
Aug 05, 2014
Timon Gehr
Aug 05, 2014
Jeremy Powers
Aug 05, 2014
David Bregman
Aug 05, 2014
Jeremy Powers
Aug 05, 2014
bachmeier
Aug 05, 2014
David Bregman
Aug 06, 2014
Jeremy Powers
Aug 06, 2014
David Bregman
Aug 06, 2014
eles
Aug 06, 2014
Jeremy Powers
Aug 06, 2014
eles
Aug 06, 2014
Artur Skawina
Aug 06, 2014
Matthias Bentrup
Aug 06, 2014
Tofu Ninja
Aug 06, 2014
Marc Schütz
Aug 06, 2014
Artur Skawina
Aug 06, 2014
Artur Skawina
Aug 06, 2014
Artur Skawina
Aug 06, 2014
Marc Schütz
Aug 06, 2014
Marc Schütz
Aug 06, 2014
Marc Schütz
Aug 06, 2014
Matthias Bentrup
Aug 06, 2014
Timon Gehr
Aug 06, 2014
David Bregman
Aug 07, 2014
David Bregman
Aug 07, 2014
David Bregman
Aug 07, 2014
David Bregman
Aug 07, 2014
David Bregman
Aug 07, 2014
David Bregman
Aug 07, 2014
David Bregman
Aug 09, 2014
Timon Gehr
Aug 10, 2014
Timon Gehr
Aug 12, 2014
Kagamin
Aug 12, 2014
Kagamin
Aug 12, 2014
Kagamin
Aug 13, 2014
Ola Fosheim Gr
Aug 07, 2014
Matthias Bentrup
Aug 07, 2014
Matthias Bentrup
Aug 07, 2014
Matthias Bentrup
Aug 06, 2014
Marc Schütz
Aug 06, 2014
Matthias Bentrup
Aug 06, 2014
Matthias Bentrup
Aug 06, 2014
Artur Skawina
Aug 06, 2014
Jeremy Powers
Aug 06, 2014
Marc Schütz
Aug 06, 2014
Mike Parker
Aug 06, 2014
David Bregman
Aug 06, 2014
eles
Aug 06, 2014
eles
Aug 05, 2014
David Bregman
Aug 05, 2014
Walter Bright
Aug 05, 2014
H. S. Teoh
Aug 05, 2014
Walter Bright
Aug 05, 2014
David Bregman
Aug 05, 2014
H. S. Teoh
Aug 05, 2014
Araq
Aug 05, 2014
Walter Bright
Aug 05, 2014
Ary Borenszweig
Aug 05, 2014
Marc Schütz
Aug 05, 2014
H. S. Teoh
Aug 05, 2014
Ary Borenszweig
Aug 05, 2014
Walter Bright
Aug 05, 2014
Marc Schütz
Aug 05, 2014
H. S. Teoh
Aug 06, 2014
Marc Schütz
Aug 06, 2014
Walter Bright
Aug 07, 2014
Marc Schütz
Aug 06, 2014
Sean Kelly
Aug 06, 2014
David Bregman
Aug 06, 2014
Sean Kelly
Aug 06, 2014
David Bregman
Aug 06, 2014
Sean Kelly
Aug 06, 2014
David Bregman
Aug 06, 2014
Sean Kelly
Aug 06, 2014
David Bregman
Aug 06, 2014
Walter Bright
Aug 07, 2014
Sean Kelly
Aug 07, 2014
Walter Bright
Aug 07, 2014
Johannes Pfau
Aug 06, 2014
Walter Bright
Aug 07, 2014
Johannes Pfau
Aug 07, 2014
Walter Bright
Aug 07, 2014
Daniel Murphy
Aug 07, 2014
H. S. Teoh
Aug 07, 2014
Sean Kelly
Aug 07, 2014
Daniel Murphy
Aug 07, 2014
H. S. Teoh
Aug 08, 2014
Daniel Murphy
Aug 08, 2014
H. S. Teoh
Aug 08, 2014
Vlad Levenfeld
Aug 09, 2014
Jonathan M Davis
Aug 07, 2014
H. S. Teoh
Aug 08, 2014
Daniel Murphy
Aug 07, 2014
Sean Kelly
Aug 08, 2014
Daniel Murphy
Aug 10, 2014
Walter Bright
Aug 10, 2014
H. S. Teoh
Aug 10, 2014
Walter Bright
Aug 10, 2014
Walter Bright
Aug 07, 2014
Sean Kelly
Aug 07, 2014
David Gileadi
Aug 07, 2014
Marc Schütz
Aug 07, 2014
Walter Bright
Aug 07, 2014
Marc Schütz
August 03, 2014
I am creating this thread because I believe the other ones [1,6] have gotten too bogged down in minutiae and the big picture has gotten lost.

Walter has proposed a change to D's assert function as follows [1]:
"The compiler can make use of assert expressions to improve optimization, even in -release mode."

I would like to raise a series of questions, comments, and potential objections to this proposal which I hope will help clarify the big picture.

1. Who and Why? What is the impetus behind this proposal? What is the case for it? Walter made strong statements such as "there is inexorable pressure for this", and "this will happen", and I am wondering where this is coming from. Is it just Walter? If not, who or what is pushing this idea? (the 'yea' side, referred to below)

2. Semantic change.
The proposal changes the meaning of assert(), which will result in breaking existing code. Regardless of philosophizing about whether or not the code was "already broken" according to some definition of assert, the fact is that shipping programs that worked perfectly well before may no longer work after this change.
Q2a. In other areas, code breakage has recently been anathema. Why is this case different?
Q2b. Has any attempt been made to estimate the impact of this change on existing code? Has code breakage been considered in making this proposal?
2c. I note that the proposal also breaks with (at least) one of D's stated "Major Design Goals".[2] ("Where D code looks the same as C code, have it either behave the same or issue an error.")

3. Undefined behavior.
The purpose of the proposal is to improve code generation, and this is accomplished by allowing the compiler to generate code with arbitrary (undefined) behavior in the case that the assertion does not hold. Undefined behavior is well known to be a source of severe problems, such as security exploits[3,4], and so-called "heisenbugs"[5].
3a. An alternate statement of the proposal is literally "in release mode, assert expressions introduce undefined behavior into your code in if the expression is false".
3b. Since assert is such a widely used feature (with the original semantics, "more asserts never hurt"), the proposal will inject a massive amount of undefined behavior into existing code bases, greatly increasing the probability of experiencing problems related to undefined behavior.
Q3c. Have the implications of so much additional undefined behavior been sufficiently considered and weighed with the performance benefits of the proposal?
Q3d. How can the addition of large amounts of undefined behavior be reconciled with D's Major Design Goals #2,3,5,15,17? [2]?
3f. I note that it has been demonstrated in the other threads that the proposal as it stands can even break the memory safety guarantee of @safe code.

4. Performance.
Q4a. What level of performance increases are expected of this proposal, for a representative sample of D programs?
Q4b. Is there any threshold level of expected performance required to justify this proposal? For example, if a study determined that the average program could expect a speedup of 0.01% or less, would that still be considered a good tradeoff against the negatives?
Q4c. Have any works or studies, empirical or otherwise, been done to estimate the expected performance benefit? Is there any evidence at all for a speedup sufficient to justify this proposal?
Q4d. When evaluating the potential negative effects of the proposal on their codebase, D users may decide it is now too risky to compile with -release. (Even if their own code has been constructed with the new assert semantics in mind, the libraries they use might not). Thus the effect of the proposal would actually be to decrease the performance of their program instead of increase it. Has this been considered in the evaluation of tradeoffs?

5. High level goals
The feedback so far demonstrates that the proposal is controversial at least. While I do not endorse democratic or design-by-committee approaches to language design, I do think it is relevant if a large subset of users have issues with a proposal. Note that this is not bikeshedding, I believe it has now been sufficiently demonstrated there are real concerns about real negative effects of the proposal.
5a. Is this proposal the best way to go or is there an alternative that would achieve the same goals while satisfying both sides?
5b. Has the 'yea' side been sufficiently involved in this discussion? Are they aware of the tradeoffs? Mostly what I've seen is Walter defending the yea side from the perspective that the decision has already been made. Maybe if the yea side was consulted, they might easily agree to an alternative way of achieving the improved optimization goal, such as creating a new function that has the proposed semantics.

References:
[1]: http://forum.dlang.org/thread/lrbpvj$mih$1@digitalmars.com
[2]: http://dlang.org/overview.html
[3]: http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html
[4]: http://blog.regehr.org/archives/213
[5]: http://en.wikipedia.org/wiki/Heisenbug
[6]: http://forum.dlang.org/thread/jrxrmcmeksxwlyuitzqp@forum.dlang.org
August 03, 2014
Thanks for the summary. I apologize for the uninformed question, but is it possible to explain how the change wrt assert will break existing code? Those details are probably buried in the extensive threads you've referenced. I ask because my understanding of assert has always been that you should use it to test your programs but not rely on it at runtime.


On Sunday, 3 August 2014 at 19:47:27 UTC, David Bregman wrote:
> I am creating this thread because I believe the other ones [1,6] have gotten too bogged down in minutiae and the big picture has gotten lost.
>
> Walter has proposed a change to D's assert function as follows [1]:
> "The compiler can make use of assert expressions to improve optimization, even in -release mode."
>
> I would like to raise a series of questions, comments, and potential objections to this proposal which I hope will help clarify the big picture.
>
> 1. Who and Why? What is the impetus behind this proposal? What is the case for it? Walter made strong statements such as "there is inexorable pressure for this", and "this will happen", and I am wondering where this is coming from. Is it just Walter? If not, who or what is pushing this idea? (the 'yea' side, referred to below)
>
> 2. Semantic change.
> The proposal changes the meaning of assert(), which will result in breaking existing code. Regardless of philosophizing about whether or not the code was "already broken" according to some definition of assert, the fact is that shipping programs that worked perfectly well before may no longer work after this change.
> Q2a. In other areas, code breakage has recently been anathema. Why is this case different?
> Q2b. Has any attempt been made to estimate the impact of this change on existing code? Has code breakage been considered in making this proposal?
> 2c. I note that the proposal also breaks with (at least) one of D's stated "Major Design Goals".[2] ("Where D code looks the same as C code, have it either behave the same or issue an error.")
>
> 3. Undefined behavior.
> The purpose of the proposal is to improve code generation, and this is accomplished by allowing the compiler to generate code with arbitrary (undefined) behavior in the case that the assertion does not hold. Undefined behavior is well known to be a source of severe problems, such as security exploits[3,4], and so-called "heisenbugs"[5].
> 3a. An alternate statement of the proposal is literally "in release mode, assert expressions introduce undefined behavior into your code in if the expression is false".
> 3b. Since assert is such a widely used feature (with the original semantics, "more asserts never hurt"), the proposal will inject a massive amount of undefined behavior into existing code bases, greatly increasing the probability of experiencing problems related to undefined behavior.
> Q3c. Have the implications of so much additional undefined behavior been sufficiently considered and weighed with the performance benefits of the proposal?
> Q3d. How can the addition of large amounts of undefined behavior be reconciled with D's Major Design Goals #2,3,5,15,17? [2]?
> 3f. I note that it has been demonstrated in the other threads that the proposal as it stands can even break the memory safety guarantee of @safe code.
>
> 4. Performance.
> Q4a. What level of performance increases are expected of this proposal, for a representative sample of D programs?
> Q4b. Is there any threshold level of expected performance required to justify this proposal? For example, if a study determined that the average program could expect a speedup of 0.01% or less, would that still be considered a good tradeoff against the negatives?
> Q4c. Have any works or studies, empirical or otherwise, been done to estimate the expected performance benefit? Is there any evidence at all for a speedup sufficient to justify this proposal?
> Q4d. When evaluating the potential negative effects of the proposal on their codebase, D users may decide it is now too risky to compile with -release. (Even if their own code has been constructed with the new assert semantics in mind, the libraries they use might not). Thus the effect of the proposal would actually be to decrease the performance of their program instead of increase it. Has this been considered in the evaluation of tradeoffs?
>
> 5. High level goals
> The feedback so far demonstrates that the proposal is controversial at least. While I do not endorse democratic or design-by-committee approaches to language design, I do think it is relevant if a large subset of users have issues with a proposal. Note that this is not bikeshedding, I believe it has now been sufficiently demonstrated there are real concerns about real negative effects of the proposal.
> 5a. Is this proposal the best way to go or is there an alternative that would achieve the same goals while satisfying both sides?
> 5b. Has the 'yea' side been sufficiently involved in this discussion? Are they aware of the tradeoffs? Mostly what I've seen is Walter defending the yea side from the perspective that the decision has already been made. Maybe if the yea side was consulted, they might easily agree to an alternative way of achieving the improved optimization goal, such as creating a new function that has the proposed semantics.
>
> References:
> [1]: http://forum.dlang.org/thread/lrbpvj$mih$1@digitalmars.com
> [2]: http://dlang.org/overview.html
> [3]: http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html
> [4]: http://blog.regehr.org/archives/213
> [5]: http://en.wikipedia.org/wiki/Heisenbug
> [6]: http://forum.dlang.org/thread/jrxrmcmeksxwlyuitzqp@forum.dlang.org

August 03, 2014
On Sunday, 3 August 2014 at 20:05:22 UTC, bachmeier wrote:
> Thanks for the summary. I apologize for the uninformed question, but is it possible to explain how the change wrt assert will break existing code? Those details are probably buried in the extensive threads you've referenced. I ask because my understanding of assert has always been that you should use it to test your programs but not rely on it at runtime.
>

Yes, it was discussed in the threads. The basic way it happens is something like this:

assert(x);
if(!x) {
 // some side effect on the program
 // the optimizer will remove this path under the proposal
}

It's much more insidious if the assert and the if are separated by some distance, such as being in different functions or even modules.

for example:

assert(x < 1000); // this assert is wrong, but has never been hit during testing. unfortunate but real life programs are not bug free.
someFunction(x);

now suppose someFunction is a library sort of function, coded in "defensive programming" style. It does something important so it validates its input first to make sure nothing bad happens. But now someFunction is inlined, and code is generated with the (wrong) assumption that x<1000. The input validation checks are removed by the optimizer. As a result, someFunction runs with invalid input, and [user's harddrive is formatted, hacker gains root access, etc].


There are other ways too. The code does not explicitly need to have an if statement checking for !x to be broken by this - any implicit checks, any kind of control flow structures can be broken just the same.
August 03, 2014
Am 03.08.2014 22:05, schrieb bachmeier:
> Thanks for the summary. I apologize for the uninformed question, but is
> it possible to explain how the change wrt assert will break existing
> code? Those details are probably buried in the extensive threads you've
> referenced. I ask because my understanding of assert has always been
> that you should use it to test your programs but not rely on it at runtime.
>

Example:
  assert(x !is null);
  if(x !is null) { x.foo = 42; }

in release mode, the assert() will (as to Walters plans) tell the compiler, that x cannot be null, so it will optimize the "if(x !is null)" away.
Currently this would not happen and this code won't segfault, with that optimization it would, in case in release mode x is null after all in some circumstance that hasn't occured during testing.

Walters stance on this is, that if x is null under some circumstance, the program is buggy anyway and in an undefined state.
Furthermore he interprets "assert()" as "the programmer asserts (as in promises) that the given condition is true".
Other people say that assert(), as it's implemented in many programming languages (including D up to now), just is a runtime check with a funny name that can be deactivated (e.g. for/in release builds).
Some have proposed to have an assume() that does what Walter wants assert() to do.

Cheers,
Daniel
August 03, 2014
On Sunday, 3 August 2014 at 19:47:27 UTC, David Bregman wrote:

> Walter has proposed a change to D's assert function as follows [1]:
> "The compiler can make use of assert expressions to improve optimization, even in -release mode."

Hmm. I really really do like that idea.

I suspect it is one of those ideas of Walter's that has consequences that reach further than anyone foresees..... but that's OK, because it is fundamentally the correct course of action, it's implications foreseen and unforeseen will be correct.

One "near term" implication is to permit deeper static checking of the code.

Both in terms of "Well, actually there is a code path in which the assert expression could be false, flag it with a warning" and in terms of "There is a code path which is unused / incorrect / erroneous is the assert expression is true", flag as an error/warning.

Furthermore, in the presence of the deeper compile time function evaluation, I suspect we will get deeper and even more suprising consequences from this decision.

Suddenly we have, at compile time, an expression we know to be true, always, at run time. Thus where possible, the compiler can infer as much as it can from this.

The implications of that will be very very interesting and far reaching.

As I said, this choice will have very far reaching and unforeseen and unforeseeable consequences.... but that's OK, since it is fundamentally the correct choice, those consequences will be correct too.
August 03, 2014
On 8/3/14, 2:57 PM, John Carter wrote:
> On Sunday, 3 August 2014 at 19:47:27 UTC, David Bregman wrote:
>
>> Walter has proposed a change to D's assert function as follows [1]:
>> "The compiler can make use of assert expressions to improve
>> optimization, even in -release mode."
>
> Hmm. I really really do like that idea.
>
> I suspect it is one of those ideas of Walter's that has consequences
> that reach further than anyone foresees..... but that's OK, because it
> is fundamentally the correct course of action, it's implications
> foreseen and unforeseen will be correct.

Agreed. One related point that has been discussed only a little is the competitive aspect of it all. Generating fast code is of paramount importance for D's survival and thriving in the market. Competition in language design and implementation is acerbic and only getting more cutthroat. In the foreseeable future efficiency will become more important at scale seeing as data is growing and frequency scaling has stalled.

Availing ourselves of a built-in "assert" that has a meaning and informativeness unachievable to e.g. a C/C++ macro is a very important and attractive competitive advantage compared to these and other languages.

Walter has always meant assert the way he discusses it today. Has he (and subsequently he and I) been imprecise in documenting it? Of course, but that just means it's Tuesday.

That said, should we proceed carefully about realizing this advantage? Of course; that's a given. But I think it's very important to fully understand the advantages of gaining an edge over the competition.


Andrei

August 03, 2014
On Sunday, 3 August 2014 at 20:05:22 UTC, bachmeier wrote:

>> 3. Undefined behavior.

Actually I have had an extensive battle within my own workplace on this subject and I think I have a reasonable insight in to both points of view.

It comes down to two opposing view of what we use asserts for.

My view, which I think corresponds with Walter's and Betrand Meyer's, is that asserts define what correct behaviour is.

If an assert fires, your program is fundamentally defective in a manner that can only be corrected by a new version of the program.

And the sooner you know that, preferably at compile time, the better.

Continuing past such an assert inevitably results in defective, possibly catastrophic, possibly flaky behaviour.

In the opposing view, an assert statement is a debug aid. In the same category as a logging printf.

If it fires, it's "Huh. That's interesting. I didn't think that would happen, but OK, it does. Cool."

Alas, these two uses have been given the same name. assert.

One resolution would be to create two assert interfaces, one that the compiler pays attention to, and one that is just a "Huh. That's interesting, I didn't expect that."

August 03, 2014
On Sunday, 3 August 2014 at 21:57:08 UTC, John Carter wrote:

> One "near term" implication is to permit deeper static checking of the code.
> Both in terms of "Well, actually there is a code path in which the assert expression could be false, flag it with a warning" and in terms of "There is a code path which is unused / incorrect / erroneous is the assert expression is true", flag as an error/warning.
>
> Furthermore, in the presence of the deeper compile time function evaluation, I suspect we will get deeper and even more suprising consequences from this decision.
>
> Suddenly we have, at compile time, an expression we know to be true, always, at run time. Thus where possible, the compiler can infer as much as it can from this.
>
> The implications of that will be very very interesting and far reaching.

I totally agree, static analysis tools should consider information contained in asserts. In the case of C/C++, I believe many of the analysis tools already do this. That doesn't mean it's a good idea for this information to be used for optimization though, for reasons explained in the OP.

> As I said, this choice will have very far reaching and unforeseen and unforeseeable consequences.... but that's OK, since it is fundamentally the correct choice, those consequences will be correct too.

This is mystical sounding gibberish. If the consequences are unforseen and unforseeable, then by definition you cannot forsee that they are correct.
August 03, 2014
On Sunday, 3 August 2014 at 21:57:08 UTC, John Carter wrote:
> consequences that reach further than anyone foresees..... but that's OK, because it is fundamentally the correct course of action, it's implications foreseen and unforeseen will be correct.

The implications are foreseen.

Any assert that will depend on any kind of notion of progress over time risk blowing up random logic undetected with a decent optimizer (> dmd).

But go ahead. This will lead to a fork.
August 03, 2014
On Sunday, 3 August 2014 at 22:15:52 UTC, Andrei Alexandrescu wrote:
> On 8/3/14, 2:57 PM, John Carter wrote:
>> On Sunday, 3 August 2014 at 19:47:27 UTC, David Bregman wrote:
>>
>>> Walter has proposed a change to D's assert function as follows [1]:
>>> "The compiler can make use of assert expressions to improve
>>> optimization, even in -release mode."
>>
>> Hmm. I really really do like that idea.
>>
>> I suspect it is one of those ideas of Walter's that has consequences
>> that reach further than anyone foresees..... but that's OK, because it
>> is fundamentally the correct course of action, it's implications
>> foreseen and unforeseen will be correct.
>
> Agreed.

I hope that agree was referring to some bits from the other paragraphs, and that you don't seriously agree with that blatantly self contradictory statement about unforseeable unforseens :p

> One related point that has been discussed only a little is the competitive aspect of it all. Generating fast code is of paramount importance for D's survival and thriving in the market. Competition in language design and implementation is acerbic and only getting more cutthroat. In the foreseeable future efficiency will become more important at scale seeing as data is growing and frequency scaling has stalled.

Would you care to address the questions about performance raised in the OP?

>
> Availing ourselves of a built-in "assert" that has a meaning and informativeness unachievable to e.g. a C/C++ macro is a very important and attractive competitive advantage compared to these and other languages.

Not really, you can redefine the C macro to behave exactly as proposed, using compiler specific commands to invoke undefined behavior. Didn't you say in the other thread that you tried exactly that?

> Walter has always meant assert the way he discusses it today. Has he (and subsequently he and I) been imprecise in documenting it? Of course, but that just means it's Tuesday.
>
> That said, should we proceed carefully about realizing this advantage? Of course; that's a given. But I think it's very important to fully understand the advantages of gaining an edge over the competition.

Please comment on the concerns raised by the OP.

« First   ‹ Prev
1 2 3 4 5 6 7 8 9 10 11