Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

We Have Mail


April 2001/We Have Mail


Letters to the editor may be sent via email to [email protected], or via the postal service to Letters to the Editor, C/C++ Users Journal, 1601 W. 23rd St., Ste 200, Lawrence, KS 66046-2700.


Dear Marc,

It’s always a great pleasure going through high quality contents of CUJ. This month’s articles were also superb! The Java Solutions section is a very good addition.

Regarding the article on "Java Named Semaphores" (Java Solutions, December 2000), there are few points I wanted to talk about:

“When a thread executes wait, one of two things happens. If no other thread has executed wait, wait doesn’t block, and the thread passes through, gaining access to the critical code. On the other hand, if another thread has already executed wait, processing on the current thread is suspeneded...”

That’s actually not the case. wait always blocks, even if no other thread has executed wait. The semantics of wait is not similar to a mutex (as mentioned in the article) but rather similar to Event objects (Windows) or condition variables (Unix).

The sample program below illustrates this point:

public class Test
{
  synchronized void showWait()
  {
    try
    {
      System.out.println
        ("calling wait");
      wait();
      System.out.println
        ("out of wait");
    }
    catch(InterruptedException e){}
  }

  public static void
    main(String[] args)
    {
      Test t=new Test();
      t.showWait();
    }
}

Here the main thread will be blocked forever, until it is notified.

The Mutex class presented is semantically not a mutex. This class requires one thread to call lock while some other thread calls unlock, whereas mutex semantics require that the thread that acquired the lock should free the lock. Hence it is more like an Event object.

Similarly, the article mentions the following regarding notifyAll:

“notifyAll...Then if multiple threads are waiting, then one is arbitrarily chosen to discontinue waiting...”

notifyAll actually wakes up all the threads (waiting on that object) and not any one. Hence, the Mutex code presented will not work, as it calls notifyAll, which would wake up all threads waiting on this mutex. The code below demonstrates notifyAll:

public class Test
{
  synchronized void showWait()
  {
    try
    {
      String threadName=
        Thread.currentThread().
          getName();
        System.out.println
        ("calling wait() in thread->"
          + threadName);
        wait();
        System.out.println
        ("out of wait in thread->"
          + threadName);
      }
      catch(InterruptedException e){}
  }

  synchronized void showNotifyAll()
  {
    String threadName=
      Thread.currentThread().
        getName();
    System.out.println
("Sleeping for 5 seconds in thread->"
      + threadName);
     try{Thread.sleep(5000);}
     catch(InterruptedException e){}
     System.out.println
("calling notifyAll from thread->"
      + threadName);
     notifyAll();
  }

  public static void
  main(String[] args)
  {
    final Test t=new Test();
    new Thread(){
      public void run()
      {
        t.showWait();
      }
    }.start();
    new Thread(){
      public void run()
      {
        t.showWait();
      }
    }.start();
    new Thread(){
      public void run()
      {
        t.showNotifyAll();
      }
    }.start();
    t.showWait();
  }
}

Sincerly,

Vishal Kochhar

The author has confirmed that his explanation of wait and notifyAll was incorrect, but hopes it does not put a damper on the main point of the article, which was creating named semaphores in Java. Thank you for your comments. mb


Dear Sir or Madam,

As a long-time subscriber to C/C++ Users Journal (longer in fact than C++ has been in the title!), and a long-time fan of the writings of Andrew Koenig and Barbara Moo, I read with great interest their article “Using Library Algorithms” (excerpted from their new book Accelerated C++: Practical Programming by Example).

This article, their book, and other articles by such notables as Bjarne Stroustrup himself reflect the views of a new school of thought regarding the teaching of programming in C++, effectively reversing the old-school approach by starting out with high-level classes and algorithms to allow the student to write effective and useful programs from the start. This top-down approach eradicates one of the biggest hurdles of the old-school bottom-up approach, which was that the student was forced to absorb quite a lot of detailed information before being able to write useful programs. This often led to considerable frustration on the student’s part, and I must say that the new approach is indeed better in a number of ways.

However, as Jon Programming Pearls Bentley notes, we also cannot forget performance. And that brings me to the point of this letter, which is to take some small issue with the following statement from an otherwise excellent article. Disregarding the obvious typo, the statement is:

*out++ = *begin++;

is equivalent to the more verbose

*out = *begin;
++out;
++begin;

While correct in the sense that the final values are identical, the first form unavoidably results in the creation of two temporary objects (which are likely just pointers) due to the use of the post-increment operator. The cost may be small, but may also be significant in cases where performance is important or where the iterators involved are objects larger than pointers. See also Scott Meyers’ More Effective C++, Item 6, for more on this particular topic.

Admittedly, delving into the details of why pre-increment is more efficient than post-increment is definitely counter to the whole philosophy of the new teaching approach, but it seems to me that some way of incorporating efficiency concerns must be found if this new school is to result in altogether better programs.

So, to Andrew Koenig, Barbara Moo, Bjarne Stroustrup, and other worthy proponents of this excellent new approach to learning C++ I address the following question: “How can efficiency concerns be appropriately included in this new approach?” Here the key word is “appropriately” since an inappropriate concern for efficiency has almost certainly damaged more programs than any lack of efficiency ever will.

Thanks.

Randy Maddox
Consulting Software Engineer and Author
[email protected]

Andrew Koenig replies:

As long as you’re going to mention Jon Bentley, I would like to bring up his two rules for optimization:

1. Don’t do it.

2. (for experts only) Don’t do it yet.

Performance is important, but so is ease of understanding, and the two often conflict. As you point out, it is important to think about how to resolve this conflict — especially in the context of an introductory book such as Accelerated C++.

The general rule that we decided to follow in our book was to think about large-scale efficiency issues, particularly when they might cause algorithms to be quadratic rather than linear, but not to worry too much about small-scale efficiency issues such as the one you raise. There are several reasons for this decision.

First, there is a limit to how much we can expect readers to be willing to master at one time. Therefore, as Bentley suggests, we would rather defer talking about optimization issues until we can assume that our readers have gained more experience.

Second, the kind of small-scale optimizations you’re talking about here typically gain only a linear factor in execution time, which means that they will have much less of an effect in practice than the large-scale issues.

Third, our experience is that it is difficult to predict accurately the effect of small-scale optimizations without trying them on relevant implementations and measuring their effect. As an example, I tried measuring execution time on one particular implementation for the following program fragment:

vector<int> v(1000, 42);

vector<int>::iterator end = v.end();
for (int i = 0; i != 1000000; ++i) {
   vector<int>::iterator
   p = v.begin();
   while (p != end) {
      ++(*p++);
   }
}

If I change the statement

++(*p++);

to

++*p;
++p;

the program runs approximately 8 per cent faster. So you are indeed right that there is a difference — at least sometimes.

However, if I turn on compiler optimization, the program runs approximately 88 per cent faster. That is, its execution time decreases by more than a factor of eight! Moreover, the two different forms of the inner loop now run at exactly the same speed. So at least in this case, the compiler appears to be capable of generating good code from either form of the inner loop without help from the programmer.

Efficiency is one of many important ideas that programmers must eventually understand. One of the hard decisions in writing an introductory book is the order in which to cover these important ideas. Throughout Accelerated C++, we have tried to introduce at each point the ideas that would add the most to what the reader would be able to accomplish at that point. We felt that it was important to discuss the

*out++ = *begin++;

usage because it is so common that our readers would surely encounter it elsewhere, and therefore that we would enable our readers to understand programs that they might otherwise find to difficult. On the other hand, we thought that it was still too early to discuss the small-scale efficiency characteristics of this notation, because many readers would not yet be in a position to understand the issue, and because understanding it would not add much to what readers could accomplish at this point in the exposition.

Regards,

Andrew Koenig


Dear CUJ,

I was interested in the article on Debugging under GNU/Linux by Randy Zack (February 2001) since I have been using Linux since 1997. One thing that he states when he is talking about core files is that we didn’t learn anything we couldn’t have learned by executing the program itself from within gbd. That is true for a program that only takes milliseconds to run, but twice I have found core files indispensable.

The first case is when a satellite data processing program I was developing dumped after running for two hours. I would have to have spent another two hours to get to that point had I restarted using gbd. Instead, the core file had enough information to identify the line number and the variables value that caused the crash and the problem was solved in a matter of minutes.

The second case is when a program I wrote that processed a live data feed crashed. In this case, there was no way of replicating the data that caused the crash and I would not have been able to find the problem by restarting the program with gbd. Instead, by loading the core file into gdb, I was able to see the offending data string. A few minutes later, the problem was fixed and the program was back on line.

The point is, core files can be an extremely useful, if not the only way, of identifying bugs in long running (or always running) code. A core file can immediately go to the program state that caused the error condition, without taking the time to get to that point.


Dear Chuck Allison,

I found your “File Processing” article in Java Solutions (February 2001) quite educational. However, I couldn’t help noticing a few things regarding the “C++ version.”

1) The return type of function toString is missing in the function declaration. This is illegal in both C and C++ according to the latest standards of the two languages. Looking at the function body, I assume you meant void as the return type.

2) Having corrected (1), I can hardly accept the fact that this is a C++ program. It looks more like C. Although it is legal C++ code, the program contains a number of constructs that are considered poor style and/or error-prone in C++.

3) Return value of -1 from main involves implementation-defined behavior. Both languages specify that to be portable main must return zero or EXIT_SUCCESS for success or EXIT_FAILURE for failure. You need to #include <cstdlib> or <stdlib.h> for the EXIT_XXX macros.

4) You check the return value of fwrite, but ignore the return value of fread. Why? There is as much chance of failure reading from a file as there is writing to it.

Regarding item (2), I omit the details but could provide them if you’re interested. Also, I thought of including my idea of what a C++ version should look like. However, depending on the circumstances, decisions like the following would have to be made:

a) Use std::string or char[]?

b) Use return error codes or exceptions?

c) Use enums for constant class values or static members?

Again, if you’re interested, I could provide my /*one of many*/ C++ solutions.

Sincerely,

Boris Pulatov

Chuck Allison replies:

Indeed, this is just a C program, and I should have labeled it as such. (The version I post on my website will have the corrections; also, note that the file type is .c, not .cpp.) My point was of course that the Java version is much more verbose. If your C++ is version is even shorter than my C version, then that would be interesting.

I forgot the return type for toString, so it defaults to int, which is valid in C.

Your point (3) is also correct, but again, I was just comparing the size of the programs. Using the <stdlib> macros (which I am very aware of, thank you) would make the one-line program longer, and as it stands, this program will run on all Windows and Unix systems. Portability was not important to the current issue, but you are indeed correct, as also is your comment about making the program more bulletproof by checking fread.

I hope that my point of Java I/O being much more verbose than C (or even C++, which I didn’t illustrate) was made by the article, nothing more nor less.

Thanks for writing.

Chuck Allison


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.