haiku-website/static/legacy-docs/benewsletter/Issue3-30.html

716 lines
46 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Be Newsletters - Volume 3: 1998</title><link rel="stylesheet" href="be_newsletter.css" type="text/css" media="all" /><link rel="shortcut icon" type="image/vnd.microsoft.icon" href="./images/favicon.ico" /><!--[if IE]>
<link rel="stylesheet" type="text/css" href="be_newsletter_ie.css" />
<![endif]--><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="Be Newsletters" /><link rel="up" href="volume3.html" title="Volume 3: 1998" /><link rel="prev" href="Issue3-29.html" title="Issue 3-29, July 22, 1998" /><link rel="next" href="Issue3-31.html" title="Issue 3-31, August 5, 1998" /></head><body><div id="header"><div id="headerT"><div id="headerTL"><a accesskey="p" href="Issue3-29.html" title="Issue 3-29, July 22, 1998"><img src="./images/navigation/prev.png" alt="Prev" /></a> <a accesskey="u" href="volume3.html" title="Volume 3: 1998"><img src="./images/navigation/up.png" alt="Up" /></a> <a accesskey="n" href="Issue3-31.html" title="Issue 3-31, August 5, 1998"><img src="./images/navigation/next.png" alt="Next" /></a></div><div id="headerTR"><div id="navigpeople"><a href="http://www.haiku-os.org"><img src="./images/People_24.png" alt="haiku-os.org" title="Visit The Haiku Website" /></a></div><div class="navighome" title="Home"><a accesskey="h" href="index.html"><img src="./images/navigation/home.png" alt="Home" /></a></div><div class="navigboxed" id="naviglang" title="English">en</div></div><div id="headerTC">Be Newsletters - Volume 3: 1998</div></div><div id="headerB">Prev: <a href="Issue3-29.html">Issue 3-29, July 22, 1998</a>  Up: <a href="volume3.html">Volume 3: 1998</a>  Next: <a href="Issue3-31.html">Issue 3-31, August 5, 1998</a></div><hr /></div><div class="article"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="Issue3-30"></a>Issue 3-30, July 29, 1998</h2></div></div></div><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="Engineering3-30"></a>Be Engineering Insights: "Threads Don't Like Playing Ping Pong," Part I</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Pierre</span> <span class="surname">Raynaud-Richard</span></span></div></div></div><p>
The main purpose of the engineering articles is to deliver simple and
accurate technical information that's meaningful to as many people as
possible. Thus, simple articles are better than complex ones—a rule I
follow as often as I can.
</p><p>
But not today.
</p><p>
With apologies to the average developer who is never going to deal with
the subtleties I'm about to discuss, I just can't stop myself! After many
general information articles on various subjects, I felt an irresistible
urge to write some strong technical content. You've been warned, now
continue at your own risk.
</p><p>
At the heart of the BeOS graphic system lies the application server. It's
been the theater of continuous cleaning, reorganization and optimisation
during the last 12 months, and more will happen, at least until Release 6.
</p><p>
Recently, we discovered a nasty interaction between the app server and
the kernel's thread scheduler. This problem had very serious
consequences, and solving it has brought a significant performance
improvement to Release 4. It also pointed out an interesting class of
problems, that some of you could encounter in the future (if not already).
</p><p>
So in this article, we are going to study different mechanisms you can
use to synchronize threads, and try to describe their behavior under
well-defined stress conditions. For that we used one or two "worker"
threads, executing the same reference task again and again. They had to
use a synchronization mechanism to guarantee that only one thread
executed the task at a time (from now on, the reference task will be
called the "critical section").
</p><p>
To simulate the <acronym class="acronym" title="Central Processing Unit">CPU</acronym>
noise generated by other unrelated threads, we
sometimes added an additional dummy thread. The dummy thread didn't vie
for the critical section—in other words, it didn't interfere directly
with the other two threads.
</p><p>
All measurements were done on a Pentium II 350MHz in conditions as stable
and similar as possible, with most of the servers, applications and
services disabled, but with some daemons left running. In this
environment, all code that we executed was always available in the level
2 cache (if not in the level 1!). In the real world, of course, this
isn't a realistic assumption, but as you will see, studying the perfect
case is hard enough already...
</p><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id718129"></a>TEST 1—An Ideal World</h3></div></div></div><p>
In this test, one <code class="constant">B_NORMAL_PRIORITY</code> thread repeatedly executed a 6.1us
("us" = "microsecond") task that's protected by a simple semaphore:
</p><pre class="programlisting c">
while (<code class="constant">true</code>) {
<code class="function">acquire_sem</code>(<code class="varname">g_sem</code>);
<code class="function">do_critical_section</code>(6.1);
<code class="function">release_sem</code>(<code class="varname">g_sem</code>);
}
</pre><p>
We ran the loop for about two seconds and recorded the latency (time
consumption) for each lock and for each unlock. We then plotted the
results on separate graphs (i.e. one for locking, another for unlocking).
These graphs represent a very rich source of information, and we're going
to be studying them throughout most of this article.
</p><p>
The graph's horizontal axis is latency in microseconds; the vertical axis
is the hit count, or the number of locks/unlocks at a particular latency.
Both axes are exponential and elided where necessary (to fit the format
of an email, and reduce the length of this article). All graphs are
supposed to be viewed with a monospaced font.
</p><p>
This first graph shows the locking latency:
</p><pre class="screen">
-------------One thread, semaphore, locking-------------
32768| A
| AA
16384| AA
| AA
8192| AA
| AA
4096| AA
| AAA
. ...
| AAA
128| AAA
| AAA B
64| AAA BB
| AAA BB
32| AAA BBBB
| AAA BBBB
16| AAA BBBB
| AAA BBBBB C
8| AAA BBBBB C
| AAAA BBBBB C
4| AAAA BBBBB CC
| AAAA BBBBB CC
2| AAAA BBBBB # CCC # #
| AAAABBBBBB ## ## CCC # # ### # #
+!-------!-------!-------!-------!-------!-------!-------!--...--!--
0 8 16 32 64 128 256 512 2048
</pre><ul class="itemizedlist"><li><p>
The (A) spike shows the case where the semaphore is acquired without
blocking. This happens 99.6% of the time with an average latency of
3.5us.
</p></li><li><p>
In the (B) spike, a scheduler interruption occurs while <code class="function">acquire_sem()</code>
is executing, but without rescheduling (i.e. <code class="function">acquire_sem()</code> continues
after the interruption). Probability: 0.3%; average latency: 8.5us. So
both (A) and (B) represent the same phenomena, except that (B) is
delayed 5.0us by the scheduler interruption. This delay is only a part
of the more generic "scheduler noise," and so it doesn't represent any
relevant information. In further comments, we will call that phenomena
"scheduler echo"; we will identify it when needed, but we won't spend
any more time commenting about it.
</p></li><li><p>
(C) is another component of the noise, weaker than the "scheduler
echo," but with an higher latency. It's probably the signature of a
daemon (most likely a real-time priority thread) that runs regularly
(from example 60 times a second) for a short amount of time (15 to
20us). Once added with the two necessary context switch latencies, that
leaves us with what we will call the "daemon echo." In this case it's
an "echo" of (A), with an addi- tional latency of 27us.
</p></li><li><p>
What's left—the scattered "#"s—is the rest of the noise
generated by either the scheduler or some other daemons whenever they
used some CPU. Although the noise delay can reach 3000us, this is
actually reasonable since it reflects quite accurately the maximum
scheduling quanta. This contains even less relevant information than
the two previously described "echoes," so from now we won't pay any
attention to it.
</p></li></ul><p>
Here's the graph for unlocking :
</p><pre class="screen">
-------------One thread, semaphore, unlocking------------------
32768| A
| AA
16384| AA
| AA
8192| AA
| AA
4096| AA
| AAA
. ...
| AAA
256| AAA
| AAA B
128| AAA BB
| AAA BBB
64| AAA BBBB
| AAA BBBBB
32| AAA BBBBB
| AAA BBBBB
16| AAA BBBBB C
| AAA BBBBB C
8| AAA BBBBB CC
| AAA BBBBBB CC
4| AAAA BBBBBB # CCC
| AAAA BBBBBB ## CCCC
2| AAAABBBBBBB ## CCCC ## # # #
| AAAABBBBBBB### CCCCC ## ### # # #
+!-------!-------!-------!-------!-------!-------!-----...--!-----
0 8 16 32 64 128 256 2048
</pre><ul class="itemizedlist"><li><p>
(A) Again, non-blocking acquisition is the overwhelming winner:
99.06% and 3.5us.
</p></li><li><p>
(B) "scheduler echo" of (A), has a higher probability (0.86%) than in
the analogous locking test, but that's because the unlocking
measurement includes the noise associated with both the unlocking and
the critical section. This is an artifact of measuring the locking and
unlocking with only two timestamps :
</p><pre class="programlisting c">
while (<code class="constant">true</code>) {
<code class="function">TIME_STAMP</code>();
<code class="function">acquire_sem</code>(<code class="varname">g_sem</code>);
<code class="function">TIME_STAMP</code>();
<code class="function">do_critical_section</code>(6.1);
<code class="function">release_sem</code>(<code class="varname">g_sem</code>);
}
</pre><p>
This allowed us to reduce the impact of time-measurement on the system,
as we can just subtract the latency of the critical section from the
second latency to get the unlocking latency. But there was no way to
avoid increasing the magnitude of the noise. Still, it's only noise --
as long as we can identify it (and ignore it), we don't care about its
magnitude.
</p></li><li><p>
(C) is the "daemon echo" of (A).
</p><p>
Round trip: More than 99% of the time, a trip through the entire loop
(acquire the semaphore, perform the critical section, release the
semaphore), takes about 13.1us (3.5 acquisition, 6.1 critical section,
3.5 release).
</p></li></ul></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id718343"></a>TEST 2—An Idealer World</h3></div></div></div><p>
We replaced the semaphore with a benaphore. You can look at one of the
previous Engineering Insights articles for more detailed information
about the benaphore:
<a class="xref" href="Issue1-26.html#Engineering1-26" title="Be Engineering Insights: Benaphores">Be Engineering Insights: Benaphores</a>.
</p><pre class="programlisting c">
while (<code class="constant">true</code>) {
if (<code class="function">atomic_add</code>(&amp;<code class="varname">g_lock</code>, 1) &gt; 0)
<code class="function">acquire_sem</code>(<code class="varname">g_ben</code>);
<code class="function">do_critical_section</code>(6.1);
if (<code class="function">atomic_add</code>(&amp;<code class="varname">g_lock</code>, -1) &gt; 1)
<code class="function">release_sem</code>(<code class="varname">g_ben</code>);
}
</pre><pre class="screen">
-------------One thread, benaphore, locking-------------
65536|A
|A
32768|AA
|AA
...
|AA
16|AA B
|AA BBB
8|AA BBB
|AAA BBB
4|AAA BBB
|AAA BBBBB C
2|AAA BBBBB CC
|AAA BBBBB CC #
+!-------!-------!-------!-------!-------!--
0 8 16 32 64 128
</pre><p>
As expected, the probability and timing are much improved:
</p><ul class="itemizedlist"><li><p>
(A) represents the main and fast case of the benaphore (when we don't
have to take the semaphore at all), with an outrageously good
probability of 99.96%. Second good news, as expected the latency is
down to 0.3us (12x faster than the semaphore).
</p></li><li><p>
(B) is the "scheduler echo" of (A). Its probability is very low
because (A) runs for such a short time that it's rarely interrupted by
the scheduler.
</p></li><li><p>
Same thing for (C), a very weak "daemon echo" of (A).
</p></li></ul><pre class="screen">
-------------One thread, benaphore, unlocking-------------
65536|A
|AA
...
|AA
256|AAA BB
|AAA BBB
128|AAA BBB
|AAA BBB
64|AAA BBBB
|AAA BBBB
32|AAA BBBB
|AAA BBBBB C
16|AAA BBBBB CC
|AAA BBBBB CC
8|AAA BBBBB CC
|AAA BBBBBB CC
4|AAA BBBBBB CCC
|AAA BBBBBBB CCC #
2|AAA BBBBBBBBB CCC ## ## # # #
|AAA BBBBBBBBBBB ## # CCCC ## ## ### ## # #
+!-------!-------!-------!-------!-------!-------!-----...--!-----
0 8 16 32 64 128 256 2048
</pre><ul class="itemizedlist"><li><p>
(A) is the logical mirror of the previous (A), with a similarly good
latency of 0.3us.
</p></li><li><p>
(B) "scheduler echo" of (A), is stronger than the previous (B). This
was expected, because we get the noise for both the unlocking and the
critical section.
</p></li><li><p>
Same comment for both (C), the "daemon echo" of (A), and (#) the
other noise.
</p></li></ul><p>
Round-trip: Greater than 99% probability of a 6.7us total round-trip --
pretty good for a 6.1us critical section. But, then again, this is an
ideal—and so unrealistic—test.
</p><p>
NOTE: In all our tests, the unlocking statistics are mostly similar to
one or the other of the two already shown. Unlocking never blocks so, as
you might expect, it doesn't really affect overall system performance. So
as there isn't too much to be learned from those, we won't show any
unlocking graphs for any of the other tests, but will only make some
comments when necessary.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id718489"></a>TEST 3—A Small Dose of Reality</h3></div></div></div><p>
Let's see what happens when we introduce a second <code class="constant">B_NORMAL_PRIORITY</code>
thread. We'll have two equivalent threads fighting over the lock. First,
we use a semaphore:
</p><pre class="screen">
-------------Two threads, semaphore, locking-------------
16384| A
. .
| A
2048| AA
| B AA
1024| BB AA
. .. ..
256| BB AA C
| BB AA CC
128| BBB AA CC
| BBB AA CC
64| BBB AA CC
. ... .....
16| BBB AACCC D
| BBB AACCCC D
8| BBB AACCCC DD # #
| BBB AACCCC DD # #
4| BBB # AACCCC DDD # # #
| BBB # AACCCC DDDD # # # #
2| BBB # # AACCCCC DDDDD # ## # #
| BBB # ## ##AAAACCCCCDDDDDD ## ## # ## # #
+!-------!-------!-------!-------!-------!-------!-----...--!----
0 8 16 32 64 128 256 2048
</pre><p>
NOTE: This is a graph of only one of the threads. The profile of the
other thread is very similar, as expected, with the same spikes in term
of both probability and latency:
</p><pre class="screen">
(A) : 24.138us (85.677%) against 24.168us (85.285%)
(B) : 3.545us (11.741%) against 3.543us (12.159%)
(C) : 29.059us ( 2.303%) against 28.878us ( 2.236%)
(D) : 52.718us ( 0.137%) against 51.378us ( 0.157%)
</pre><p>
So from now on, we won't comment about the second thread at all.
</p><ul class="itemizedlist"><li><p>
(A) The most common case is no longer unblocked acquisition. Instead,
the threads are starting to "ping pong": 85.6% of time, the thread must
wait to acquire the semaphore, with a latency of 24.1us. That time
includes the first thread blocking, a context switch, the second thread
unblocking, the second thread executing the critical section, the
second thread blocking, another context switch and finally our first
thread unblocking. That also implies that the context switch takes
about 5us, a very impressive number, but not very realistic as in this
case everything clearly happens in the level 1 cache.
</p></li><li><p>
(B) is the unblocked acquisition spike. The latency is logically the
same as in the one-thread test (3.5us), but the probability has dropped
to 11.7%. This is easy to explain: whenever the scheduler suspends both
threads in a state where no one owns the semaphore, whatever thread
starts first is free from any synchronization issue, as if it was
alone. And that lasts until the scheduler suspends it again, which most
likely puts the system back into "ping pong" mode.
</p></li><li><p>
(C &amp; D) are the "scheduler echo" and "daemon echo" of (A).
</p></li></ul><p>
Round-trip: 24.1 + 6.1 + 5.0 = 35.1us for 7/8 of the iterations. The
"ping pong" clearly affected the performance, but it's not a total
disaster compared to the 13.1 * 2 (don't forget, now we have 2 threads
working in parallel!) = 26.2us of the semaphore.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id718563"></a>TEST 4—The Benaphore Curiosity</h3></div></div></div><p>
Mostly out of curiosity, let's see what happens when we use a benaphore
in the two threads context.
</p><pre class="screen">
-------------Two threads, benaphore, locking-------------
16384| A
. .
| A
2048|B AA
|B AA
1024|BB AA
|BB AAA
512|BB C AAA
|BB C AAA
256|BB CC AAA D
... .. ... .
32|BB CC AAADD
|BBCCC AAADD
16|BBCCC AAADD E
|BBCCC AAADDD E
8|BBCCC AAADDD # E
|BBCCC AAADDD # EE
4|BBCCC AAADDD #EEE
|BBCCC # AAADDD #EEE # #
2|BBCCCC # AAAADDD #EEE # ### #
|BBCCCC ## AAAAADDDD #EEE ####### # # ## ##
+!-------!-------!-------!-------!-------!-------!-----...--!----
0 8 16 32 64 128 256 2048
</pre><ul class="itemizedlist"><li><p>
(A, D &amp; E) The ping pong mode and its two "echoes" comprise 82% of
the iterations with a latency of 24.7us (+5.0us and +27.0us for the
echoes).
</p></li><li><p>
(B) Non-blocking acquisition comprises 14.3% at 0.31us (a result
similar to the previous non-blocking acquisition for two threads using
a semaphore, but just faster as we have a benaphore now).
</p></li><li><p>
(C) This aberration is the result of a race condition in which the
benaphore becomes a semaphore: Thread A sets the benaphore counter (bc)
to 1 and enters the critical section. Thread B comes along, sets bc to
2, and heads for <code class="function">acquire_sem()</code>—BUT—the scheduler switches to
thread A before thread B gets to actually invoke <code class="function">acquire_sem()</code>. Thread
A leaves the critical section, decrements bc to 1 and calls
<code class="function">release_sem()</code>. Since no one is actually waiting for the semaphore
(since thread B never got that far), thread A sets the semaphore
counter to 1. After that, thread A increments bc to 2 and call
<code class="function">acquire_sem()</code>. Since the semaphore has already been released, thread A
acquires it without blocking. Then it passes through the critical
section, decrements bc to 1, releases the semaphore... and so on until
the scheduler decides it's B's turn. In other words, all those
<code class="function">acquire_sem()</code> and <code class="function">release_sem()</code>
calls were unnecessary, but they don't
really hurt all that much (it's still better than playing ping pong).
</p></li></ul><p>
As expected, in ping pong mode the benaphore is slightly slower than the
semaphore (24.7us vs. 24.1us), but the non-blocking case is still much
faster (0.31us vs. 3.5us). The unlocking test (not shown) also satisfies
this expectation.
</p><p>
Round-trip: A little bit better than the semaphore. The improvement in
non-blocking performance makes up for the degraded ping pong. In real
life, it's not clear that we would get a similar behavior, but in any
case, even if the benaphore gets slower than the semaphore, it won't be
by much.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id718657"></a>First part conclusion:</h3></div></div></div><p>
When two threads have to fight for the ownership of a shared critical
section, performances can be significantly reduced. The snappy little
benaphore degenerates into a semaphore, and the CPU efficiency is reduced
by a small factor—the two-thread tests are 1.4 to 2.5 times less
efficient than the one-thread tests. This would seem bad enough already,
but who knows what's going to happen once we move those tests from a
ideal and isolated environment into a more realistic one. Clearly a story
for next week...
</p></div></div><hr class="pagebreak" /><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="DevWorkshop3-30"></a>Developers' Workshop: Mucking With Menus</h2></div><div xmlns:d="http://docbook.org/ns/docbook"><span xmlns="http://www.w3.org/1999/xhtml" class="author">By <span class="firstname">Owen</span> <span class="surname">Smith</span></span></div></div></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id718701"></a>Foreword</h3></div></div></div><p>
Harlan Ellison once noted that first impressions are deadly, dangerous,
and deceiving. Since this is my debut Newsletter article, I'm aware of
what's at stake. I've had my hair cut. I've shaved my face raw and put a
styptic pen in my pocket for the nicks. I'm wearing that same collared
shirt, spiffy tie, and cotton slacks that almost lost me the job when I
interviewed here. All this so that you'll walk away from this column with
that warm, fuzzy, BeOS-coding kind of feeling. So much for introductions.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id718712"></a>Delicatessens, Laminates, and American Consumers</h3></div></div></div><p>
An odd phenomenon occurs after midnight on summer weekends here in the
land of suburban sprawl. In eerie emulation of the coastal grunion runs,
hordes of Hollywood zombies issue forth from their movie theater coffins.
At that time of night activities are scarce—absent certain chemical
and hormonal addictions, and/or a zoot suit and the irrepressible urge to
dance.
</p><p>
For the rest of us there's the 24-hour delicatessen. Some go for the
food; some go to avoid the grunion. Me, I head to the deli to marvel at
one thing: a laminated, folded sheet of cardboard stock which, when
unfolded, stands two feet high and three feet wide. This item is not just
a menu—it's a symbol of the bewildering diversity of American culture,
of the consumer's RIGHT TO CHOOSE. Confronted by this formidable
selection, I forego exercising my birthright and always get the corned
beef Reuben and the vanilla Coke.
</p><p>
So what has that got to do with the BeOS, you ask? The connection is
menus. These are not just the cornerstone of the contemporary GUI; they
also show off BMessages and object- oriented design to good effect. And,
they provide a safe, wholesome springboard to the wonderful world of the
Interface Kit (see the foreword on the Importance of First Impressions).
</p><p>
To whet your appetite for menus, I've concocted an example of an
application that tweaks menus a bit more than the average Be application.
Appropriately enough, it's called <span class="application">MenuWorld</span>. Go ahead, download it:
</p><p>
ftp://ftp.be.com/pub/samples/interface_kit/MenuWorld.zip
</p><p>
The following notes boil down the essence of this example.
</p></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id718763"></a>The Four Corners</h3></div></div></div><p>
Ninety percent of menu behavior is defined in four classes:
</p><div class="orderedlist"><ol><li><p>
<code class="classname">BMessage</code>:<br />A command that is executed when a menu item is selected.
If you're unfamiliar with <code class="classname">BMessage</code>, check The Be Book Application Kit
documentation:
</p><p>
<a class="link bebook" href="../BeBook/BMessage.html">BMessage.html</a>.
</p><p>
A romantic relationship awaits you.
</p></li><li><p>
<code class="classname">BMenu</code>:<br />
A subclass of <code class="classname">BView</code> that displays a pop-up or pull- down
menu. The most commonly used functions here are <code class="methodname">AddItem()</code>,
<code class="methodname">AddSeparatorItem()</code>, and <code class="methodname">RemoveItem()</code>.
Note that <code class="methodname">AddItem()</code> takes either a
single item or a whole submenu. You've seen hierarchical menus before;
you know the drill. Also note that <code class="classname">BMenu</code>s appear and disappear in
pop-up <code class="classname">BWindow</code>s, which makes an interesting flourish to apply to other
pop-up UI widgets—such as tool tips.
</p></li><li><p>
<code class="classname">BMenuBar</code>:<br />
A subclass of <code class="classname">BMenu</code> that specifically displays a
pull-down menu bar. This is often added to a window in the window's
constructor, and automatically sizes itself quite nicely.
</p></li><li><p>
<code class="classname">BMenuItem</code>:<br />
Represents one drawable item in the menu. Although it's
not actually a view (it uses the <code class="classname">BMenu</code> it's contained in for drawing),
it's capable of invoking a command, as any decent <code class="classname">BInvoker</code> can. This
is an excellent candidate for subclassing if you've got some custom
item drawing to do (and I did; see the <span class="application">MenuWorld</span>
class <code class="classname">BitmapMenuItem</code>).
</p></li></ol></div></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id718901"></a>Primping and Preening</h3></div></div></div><p>
There are three different kinds of menus as far as drawing goes:
column-drawn (standard), row-drawn (e.g., menu bars), and custom-drawn
(called "matrix," though there's no restriction on how the items are
arranged). You specify which one you're using when you create the menu.
MenuWorld proudly presents all three methods.
</p><p>
If you're arranging menu items in a column, things couldn't be easier.
The menu works with your menu items to figure out how big to make the
menu, and how to arrange the items within the menu. The menu also takes
care of drawing submenu triangles and shortcut symbols. All you need to
do is create your menu items and add them to the menu. Working with
row-by-row organization is similar.
</p><p>
If you're doing a custom arrangement of items, things get more
interesting. Here are some points to consider:
</p><div class="orderedlist"><ol><li><p>
You need to calculate the bounds of the menu, and the frame of each item in
the menu. This can't be done directly with standard
<code class="classname">BMenuItem</code>s, so you'd better be ready to derive your
own menu item subclass, and provide some ability to calculate the item's
frame based on either its contents, the menu's dimensions, or both. (Note
that blindly calling the menu item's
<code class="methodname">GetContentSize()</code> function won't work—it's
protected, and can only be called by the <code class="classname">BMenu</code>
class, not you!) In <span class="application">MenuWorld</span>, the
<span class="guimenu">Test</span> menu displays icons, whose sizes are well defined,
so this is not difficult. Each icon is represented by the simple class
<code class="classname">BitmapMenuItem</code>, and the class
<code class="classname">TestMenuBuilder</code> does the actual work of building the
menu items and menu.
</p></li><li><p>
The padding that would be inserted around normal <code class="classname">BMenuItem</code>s in a
column arrangement to take care of the submenu triangles and shortcuts
is noticeably missing in a row or custom arrangement. So, if you want
those submenu wedges and shortcuts, or the hefty left and right
margins that these imply, you're going to need to cook them up
yourself.
</p></li></ol></div></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id718992"></a>ZPG and Other Population Paradigms</h3></div></div></div><p>
Stuffing your menus with items is easy, and can be done in several places:
</p><div class="orderedlist"><ol><li><p>
For many purposes, it's enough to add the items once at the
beginning, generally when your window is being built. The <span class="guimenu">File</span> menu in
<span class="application">MenuWorld</span> is built up this way
(see <code class="classname">MenuWindow</code>::<code class="methodname">BuildFileMenu()</code>).
</p></li><li><p>
Simple item changes traceable to a single action can be done at any
time. For instance, when the <span class="guibutton">Add Menu</span> button is clicked, the window
immediately adds the menu to the menu bar. Similarly, when the <span class="guibutton">Delete</span>
button is pressed while a top-level menu is selected, the window
immediately removes the menu from the menu bar. You can easily extend
this approach to swap in and out different menu bars from the window
as needed (the <acronym class="acronym" title="Object Linking and Embedding">OLE</acronym>
paradigm of menu-document relations comes hurtling
to mind, but try doing this in
<acronym class="acronym" title="Microsoft Foundation Classes">MFC</acronym>
under any other circumstances!). In
<span class="application">MenuWorld</span>, clicking on the check
box <span class="guilabel">Hide User Test Items</span> swaps the
menu bar that contains the user menu items for a menu bar which
doesn't.
</p></li><li><p>
If you have a fixed set of menus in the menu bar, you may want to
defer touching up the menu items until the menus are actually being
shown (i.e., in <code class="classname">BWindow</code>::<code class="methodname">MenusBeginning()</code>). Here you can enable/disable
items, or even completely build up the contents of menus. If your
menus have especially volatile contents, or the interactions that
cause menu items to be enabled or disabled are particularly
complicated, this is a great way to go. <span class="application">MenuWorld</span> demonstrates the
approach by dynamically building up the contents of each
user-specified menu in <code class="classname">MenuWindow</code>::<code class="methodname">MenusBeginning()</code>.
</p></li></ol></div></div><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id719105"></a>Magick Mother Invocation</h3></div></div></div><p>
Since <code class="classname">BMenuItem</code>s inherit from
<code class="classname">BInvoker</code>, you can pull many of the same
tricks with them as you can with controls and other invokers. In
particular, your messages can be arbitrarily complex, and you don't need
to dispatch them to the main window. For instance, the
<span class="guimenu">File</span>-&gt;<span class="guimenuitem">About...</span>
menu item dispatches to the global application object. Also note that
each user item contains the same message ID, so they all get handled in
the same place (a separate field identifies the actual name of the item
which sent the message); the test items are set up in a similar manner.
Again, try achieving that same kind of flexibility with
<acronym class="acronym">MFC</acronym>!
</p><p>
There is a price to this flexibility, however. Assuming <span class="application">MenuWorld</span> were a
full-featured menu editor, let's say we wanted to save the menus we've
created and use them in our application, à la Visual C++ menu resource
editing. The Be way to do this is to archive the Menu Bar and its
constituents into a <code class="classname">BMessage</code>, then write the flattened
<code class="classname">BMessage</code> data to a
<code class="classname">BResource</code>s object.
</p><p>
The problem, which ties in to a recent missive on BeDevTalk, is that the
<code class="classname">BMenuItem</code>s do not store their targets—indeed, they have no inherent
way to identify a target once they've been archived and reconstituted.
One thing you can do pretty easily is to set the target of each menu item
to the main window when you instantiate them. However, if you want to set
special <code class="classname">BMenuItem</code> targets in <span class="application">MenuWorld</span>,
coming up with an archiving
mechanism is non-trivial. Some third-party apps on BeWare, such as
<span class="application">AppSketcher</span> and <span class="application">Interface Elements</span>,
address this problem:
</p><p>
http://www.be.com/beware/Development.html#Cat_Interface%20Creation
</p><p>
That's it for this week. I leave you with a German blessing: "Froehlich
sei das Codeverbessern; guten Appetit!"
</p></div></div><hr class="pagebreak" /><div class="sect1"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h2 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="BeDevTalk3-30"></a>BeDevTalk Summary</h2></div></div></div><p>
BeDevTalk is an unmonitored discussion group in which technical
information is shared by Be developers and interested parties. In this
column, we summarize some of the active threads, listed by their subject
lines as they appear, verbatim, in the mail.
</p><p>
To subscribe to BeDevTalk, visit the mailing list page on our web site:
http://www.be.com/aboutbe/mailinglists.html.
</p><div class="sect2"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h3 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id719237"></a>NEW</h3></div></div></div><div class="sect3"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h4 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id719243"></a>Subject: Random not so random?</h4></div></div></div><p>
The opening parry regarded the proper seeding of the random number
generator: Call <code class="function">srandom()</code> (or <code class="function">srand()</code>)
only once, and before any <code class="function">random()</code>
(<code class="function">rand()</code>) calls. The safest bet is to set the seed in
<code class="function">main()</code>.
</p><p>
Other details popped out:
</p><ul class="itemizedlist"><li><p>
The high bits are more random than the low bits. If you want to
restrict the range of the random number, you should divide rather than
mask (or mod).
</p></li><li><p>
If you want a random number that's bigger than 16 bits, combine
multiple <code class="function">random()</code> calls.
</p></li><li><p>
<code class="function">random()</code> is more random than <code class="function">rand()</code>.
But is it truly random? Some
folks think it's about as random as you can get. Others say that
because the implementation varies between compilers, you shouldn't rely
on <code class="function">random()</code>. Instead, you should find a good algorithm and roll it
yourself.
</p></li></ul></div><div class="sect3"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h4 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id719320"></a>Subject: objects in areas</h4></div></div></div><p>
How do you put an object in a shared area so more than one application
can use it? For example, how do you share a <code class="classname">BBitmap</code>? As asked by Ingo
Weinhold:
</p><p>
<span class="quote">The only way I see is to archive the <code class="classname">BBitmap</code> object and flatten this
archive to the area. [However,] each application has to unflatten and
reinstantiate the object, which won't save any memory...</span>
</p><p>
<span class="quote">So let's assume it's possible...other applications could clone the area,
but the logical addresses may differ. An object that has stored pointers
to address its data will get into trouble if its logical address changes.
Correct?</span>
</p><p>
First things first: No, you can't share an object (not safely and
predictably, anyway). As for creating an object at a specific address,
Jon Watte reminds us that C++ provides a "placement" allocation.
</p></div><div class="sect3"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h4 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id719369"></a>Subject: (Sigh) BeOS on PPC</h4></div></div></div><p>
Announcing a grass roots movement to petition Apple to release G3 specs
to Be. The devil's advocate response from Geoffrey Clements (which, read
literally, depicts a bizarre anatomy):
</p><p>
<span class="quote">Who cares, really? Yeah a G3 running the Mac OS will kick a Pentium II
running Windows's ass. But a dual Pentium II running the BeOS will Kick
the G3's ass.</span>
</p><p>
Yes, but with a proviso:
</p><p>
<span class="quote">BeOS is a faster operating system, so long as it doesn't get klunkier in
each release. Its been rather noticeable that BeBoxen that could zip
speedily along in DR8.2 (BeOS in 1996) are almost unusably slow in R3.
I'm speculating that one of these slowdowns is due to the <acronym class="acronym">MIME</acronym> typing
file handling system.</span>
</p><p>
The proposed solution: Make <acronym class="acronym">MIME</acronym>-typing optional. "What?!?" quoth Chris
Herborth:
</p><p>
<span class="quote">How could the filesystem's <acronym class="acronym">MIME</acronym> file attributes slow down the display
and applications in R3? The filesystem in R3 is _orders_of_magnitude_
faster than the one in DR8. And it wasn't the only thing that changed
between DR8 and R3.</span>
</p><p>
<span class="quote">If the file typing is made optional, we'll have the same useless
filesystem as Windows and UNIX; our files won't know what they are, and
we'll have to rely on stupid file extensions for everything.</span>
</p></div><div class="sect3"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h4 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id719447"></a>Subject: Performance followup</h4></div></div></div><p>
A spawn of the BeOS on G3 thread, listeners discussed system performance.
Is it degrading? Is it the app server's fault? MIME types? Anti-aliased
fonts? After some discussion
</p><p>
THE BE LINE: (From George Hoffman).
</p><p>
<span class="quote">The graphics team is well aware of the state of app_server performance.
Not all of the perceived slowdown from PR2 to R3 is the fault of the
app_server, but there are hard numbers which do indicate a slight
slowdown in graphics performance. ... One of the big priorities we've had
for R4 is performance, and I'm pleased to say that the R4 development
app_server is quite a bit faster than R3, and, more importantly, _feels_
faster.</span>
</p><p>
(Also, see Pierre Raynaud-Richard's article, above.)
</p></div><div class="sect3"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h4 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id719485"></a>Subject: Broken assembly debugger on Intel R3.1?</h4></div></div></div><p>
Delesley S. Hutchins is having trouble with Be's (ahem) streamlined
debugger:
</p><p>
<span class="quote">...I can't get the debugger to work... I type "help", and nothing
happens... it doesn't even go back to the debugger prompt.</span>
</p><p>
From the scept'red isle, that other Eden, demi-paradise, royal throne of
kings and Tinky Winky, Simon Clarke suggests, moddishly:
</p><p>
“Do you have
</p><pre class="screen">
export BEDEBUG=true
</pre><p>
set in your user setup environment file? I had the same problem if I went
into the debugger from the alert that a crashed app shows. However, with
the debugger on all the time it works fine.”
</p></div><div class="sect3"><div xmlns="" xmlns:d="http://docbook.org/ns/docbook" class="titlepage"><div><div xmlns:d="http://docbook.org/ns/docbook"><h4 xmlns="http://www.w3.org/1999/xhtml" class="title"><a id="id719528"></a>Subject: Tips on maniacal code optimization</h4></div></div></div><div class="qandaset"><table border="0" summary="Q and A Set"><col align="left" width="1%" /><tbody><tr class="question"><td><a id="id719537"></a><a id="id719539"></a>Q:</td><td><p>
What's the first step along the path to optimization?
</p></td></tr><tr class="answer"><td align="left" valign="top">A:</td><td align="left" valign="top"><p>
"Profile it first!"
</p></td></tr></tbody></table></div></div></div></div></div><div id="footer"><hr /><div id="footerT">Prev: <a href="Issue3-29.html">Issue 3-29, July 22, 1998</a>  Up: <a href="volume3.html">Volume 3: 1998</a>  Next: <a href="Issue3-31.html">Issue 3-31, August 5, 1998</a> </div><div id="footerB"><div id="footerBL"><a href="Issue3-29.html" title="Issue 3-29, July 22, 1998"><img src="./images/navigation/prev.png" alt="Prev" /></a> <a href="volume3.html" title="Volume 3: 1998"><img src="./images/navigation/up.png" alt="Up" /></a> <a href="Issue3-31.html" title="Issue 3-31, August 5, 1998"><img src="./images/navigation/next.png" alt="Next" /></a></div><div id="footerBR"><div><a href="http://www.haiku-os.org"><img src="./images/People_24.png" alt="haiku-os.org" title="Visit The Haiku Website" /></a></div><div class="navighome" title="Home"><a accesskey="h" href="index.html"><img src="./images/navigation/home.png" alt="Home" /></a></div></div><div id="footerBC"><a href="http://www.access-company.com/home.html" title="ACCESS Co."><img alt="Access Company" src="./images/access_logo.png" /></a></div></div></div><div id="licenseFooter"><div id="licenseFooterBL"><a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/" title="Creative Commons License"><img alt="Creative Commons License" style="border-width:0" src="https://licensebuttons.net/l/by-nc-nd/3.0/88x31.png" /></a></div><div id="licenseFooterBR"><a href="./LegalNotice.html">Legal Notice</a></div><div id="licenseFooterBC"><span id="licenseText">This work is licensed under a
<a rel="license" href="http://creativecommons.org/licenses/by-nc-nd/3.0/">Creative
Commons Attribution-Non commercial-No Derivative Works 3.0 License</a>.</span></div></div></body></html>