Lukthil's Efficiency Coding Workshop

MUSHCode for Lukthil's Efficiency Coding Workshop

Class Name: Efficiency Coding Workshop
Class Instructor: Lukthil
Date: Wed May 8th, 1996
Time: 4:30 pm gametime until whenever
Difficulty: Advanced

Abstract:
This class is BYOC (Bring Your Own Code). Students should bring code that
they are willing to set visual. We will streamline the example code provided
by the students until it as efficient as possible. This streamlining process
will be used to explain what makes code efficient and why. We will examine
design decisions and algorithmic and computational concepts which can help
coders to code projects that meet speed and size constraints. Many elements
of the class will be Elendor-specific including (where necessary)
explanations and outlines of the latest Elendor hardcode modifications.
Familiarity with hardcode or C is not required. The class is targeted
towards students with some degree of coding skill such as mush admins,
rulers, and local admins as well as anyone else who has coded a sizeable
project in the past. Not everyone needs to bring code. Interested players
who do not have code available are welcome to sit in and listen and ask
questions. Essentially this is a no holds barred "Lukthil explains anything
you want him to" kind of class. Bring your inquisitive minds.

Transcript:

Lukthil says, "Testing 1 2 3..."
Frodo has arrived.
Sabrina has arrived.
Frodo says, "Ah... is this where the class is going to be?"
Lukthil nods.
Sabrina says, "Ah-ha!"
Sabrina was expecting it to be in the code-school class room :)
Nolindil wavers
Nolindil says, "Let me know when you really begin, so I can start logging. :)"
Across Middle-earth, Lukthil shouts, "The class posted on Code-School 3 is
happening right now in the Auditorium in the OOC School if you're
interested."
Thranduil has arrived.
Beta has arrived.
Thranduil waves and takes a seat in the back.
Gandalf says, "The first thing you should learn is how to spam the queue."
Halbarad has arrived.
Sabrina pokes Gandy for having arrived silently!
Donovan has arrived.
Lukthil begins placing exacto knives of various shapes and sizes one by one
on the table in front.
Nolindil grins.
Thranduil oohhs and gets all excited.
Danico has arrived.
Mithdae has arrived.
Sabrina's eyes grow wide and she erps quietly, holding her hands up, "Really!
I didn't mean it!"
Lukthil gingerly places a jar with someone's vital organ in it on the table.
Krutt has arrived.
Nolindil laughs
Nolindil looks around for the huge, thick printouts of MUSH profiling...
Lukthil looks at Nolindil with a curious expression on his face. "I
memorized them."
Nolindil grins
Sabrina oooohs
Lukthil says, "Okay let's begin."
There's a crackle of thunder and the door closes on its own with a slam.
Sabrina jumps out of her seat and looks around nervously.
Thranduil moves further away from da teacher.
Lukthil says, "I assume you've all read the abstract. This class is on what
comes _after_ you already know how to code stuff that works. So if
you're a newbie coder, you probably won't find this useful. It's
about why one piece of code that works is better than another that
also works. :)"
Sabrina offers a Coke to da teach if he promises not to kill her.
Mithdae has arrived.
Lifan has arrived.
Arlo has arrived.
Lukthil says, "I'll be taking examples of working code from people and we'll
streamline it together if we have time. First of all, I'd like to
start of with a few questions that I've received from people in
preparation for the class. These are very good examples of what I'd
like to talk about here today."
Danico has arrived.
Lukthil says, "Also, a little disclaimer: I may experience connection
problems near the beginning of this class but they should improve
as time goes on. The network that I pass through to connect to the
mush appears to be very lagged during business hours every day."
Gandalf says, "can you teach me to make a puppet that follows me and barks
when I say good dog?"
Sabrina giggles
Thranduil can teach you that Gandz.
Lukthil says, "Let's start with a few questions that were sent to me by
Nolindil. I'll answer them in order and if you have questions or
need additional information about any of them, just shout out your
question."
Arlo looks Nolindil over.
Lukthil says, "Gandy, I should think that would be something you would teach
a puppet by example. :)"
Gandalf says, "I meant in RL."
Lukthil says, "So did I. :)"
Thranduil grins.
Sabrina laughs
Beta has disconnected.
Lukthil says, "Okay first one..."
Lukthil says, "1) Does Elendor have what is called "short circuit evaluation"
in Turbo Pascal - Viz. when an AND() or OR() function is being
evaluated, does evaluation STOP as soon as it is clear the condition
will fail? This could save a good amount of CPU if you can figure out
how to do it with MUSH code."
Beta heads back to Middle-earth.
Paleran has arrived.
Lukthil says, "To clarify this in case anyone isn't sure what the question
means: Most languages parse arguments to an AND or OR function only
as far as necessary. I.e. Why evaluate the second argument to an
AND if the first evaluates to false? You already know it's going
to be false... etc."
Lukthil says, "Answer 1: Actually it's interesting that you would choose to
ask this question. Until last week the answer was no. The MUSH code
fully parsed all arguments to boolean functions. Naturally this is
not a very efficient way to do things, however what may surprise you
is that the difference doesn't really make much of a speed impact on
the mush has a whole. The softcode AND() and OR() functions are
relatively unused compared to most functions. (Just think about how
many times you use GET() for example or SWITCH().) Last week I
inserted a change into the code so that arguments to AND() will be
parsed until a "false" is reached and arguments to OR() will be
parsed until a "true" is reached just like most modern languages
like Turbo Pascal do. The reason the change is useful is more one
of programming options than of speed. Making the booleans operate
this way gives programmers like us a useful way to establish
conditionals in our code. For example, putting a side-effect
function in a later argument can cause that side-effect to operate
conditional upon the previous arguments to the boolean."
Belegril has arrived.
Belegril waves.
Paleran tiptoes in and takes a seat in the back with a big grin on his face :)
Gandalf says, "Ew...Cool."
Vinyesse has arrived.
Lifan has disconnected.
Lifan heads back to Middle-earth.
Lifan has left.
Mithdae has disconnected.
Lukthil says, "Essentially what this means practically to softcoders like us
is that you should try to put the SHORTEST functions in the FIRST
arguments to and() and or(). That way it will be less likely that
the later intensive functions will ever be evaluated."
Mithdae heads back to Middle-earth.
Gandalf says, "So now if I did an and() filled with a series of side affects
it would go through them all until one returned a false..."
Lukthil nods to Gandy. "Exactly."
Beta has arrived.
Arlo stands and exits the auditorium, realising this class is a bit above
his head. :)
Vinyesse raises a hand.
Arlo has left.
Lukthil says, "If you're not sure what a "false" is, it is defined under
HELP BOOLEAN VALUES I believe."
Galdor has arrived.
Mithdae has arrived.
Galdor hands Gandy his hall pass and takes a seat.
Lukthil says, "Second question.. 2) Is it better to use
switch(<condition>,<result1>,switch(<condition>,etc),<result2>,
switch(<condition>,etc.))
OR...
switch(condition/condition/condition,1/1/1,actions,1/1/0,actions,1/0/1,
actions...etc.)"
Danico says, "Could you give an example of where a Boolean would be used?"
Belegril bets on the latter. :)
Paleran raises his hand quickly.
Vinyesse grins.
Paleran would bet on the romer, but the lag prevents him from doing so.
Vinyesse blinks at Pal.
Paleran says, "romer? former..."
Thranduil just waits for the answer no needs to bet here.:P
Paleran dohs.
Lukthil says, "Let me answer Danico's question first... A simple example?
Maybe something like: say
switch(and(hasflag(%#,connected),strmatch(name(%#),G*)),1,You
are a connected player and your name begins with a G.)"
Paleran grins to Viny.
Lukthil says, "Answer to the switch() question... Answer 2: The answer to
this question calls for a design decision. If a later <condition>
is particularly intensive and an earlier <condition> is likely to
be true, you definitely want to use the first form you gave with
the nested SWITCH()'s. Since SWITCH() evaluates its arguments one
by one until a match is found, it won't tend to evaluate that
intensive <condition> unless it absolutely has to and you'll save
time. However, notice that the first form requires more functions
than the second. You have all those extra SWITCH()'es in there.
That means that if none of the <conditions> are particularly
intensive, you probably want to use the second form where you
evaluate all the <conditions> every time. You'll save time in
general because you'll have less function invocations in total
(less SWITCH()'es)."
Beta heads back to Middle-earth.
Beta has left.
Belegril says, "Ooh, too bad I don't have any obvious conditions. :)"
Lukthil says, "This brings up something that serves as a pretty good rule of
thumb as to what's fast and what's not. Usually the number of
functions you call during the execution of a routine is a pretty
good indicator of how CPU intensive your code is. This isn't a
perfect rule of course. Somethings like lsearch() are very intensive.
However it's pretty good. Also remember that when you count function
invocations, you must count functions inside loops multiple times.
Like inside an iter(), all the functions in the second argument get
evaluated the same number of times as elements in the list of the
first argument."
Belegril has reconnected.
Paleran hopes to gawd Lukthil isn't typing all this out. That's >100 wpm :P
Sabrina laughs and was thinking that too :)
Danico has disconnected.
Vinyesse grins.
Sabrina says, "but suspects that he is ;)"
Belegril thinks of Bethphel's typing speed, though. :)
Danico heads back to Middle-earth.
Belegril has partially disconnected.
Lukthil says, "That's really why iter()'s are so dangerous. You couldn't
possibly end up with on the order of thousands of function
invocations without iters. You'd wimp out while typing it. :)
But with an iter, all you need is to put a hefty number of
functions in the second argument and then something that evaluates
to a long list in the first argument, and you end up lagging the
whole mush."
Belegril heads back to Middle-earth.
Belegril has left.
Belegril has arrived.
Lukthil says, "For that reason, one should be very desirous of making the
body of iter() functions small. Loops are where you save the most
time. Other stuff will only get evaluated rarely and won't matter
nearly as much. There are of course a few places other than loops
where speed is very desireable and that will come up in one of the
later questions."
Lukthil says, "Here's the second half of question #2... On a related tangent,
is switch(<data>,<string1>,actions...etc.) better than
switch(strmatch(<data>,<string>),1,actions...etc.) ???"
Vinyesse says, "Yes?"
Lukthil says, "The answer this question is more clear: It is better to use
the SWITCH() without the STRMATCH(). Putting a STRMATCH() inside
there is redundant. The code for the SWITCH() function itself needs
to do the equivalent of a STRMATCH() anyways so actually putting
STRMATCH() in there makes it do it twice. This isn't to say that
STRMATCH() should never appear inside a SWITCH() though. Sometimes
in certain cases you'll save time by combining rather desparate
checks under one big SWITCH(). In those cases you might have
something like:
switch(1,sign(add(%0,%1)),do something,strmatch(%0,%1),do something, ...)"
Paleran hehs, "That's >1000 wpm there :) No way :)"
Sabrina laughs
Galdor says, "Luk moves in mysterious waaaaaays."
Vinyesse waves.
Gandalf says, "He is just really lagged. He started typing yesterday."
Sabrina laughs!
Nolindil grins
Thranduil chuckles.
Lukthil says, "This isn't very accurate as far as my typing speed. I lag so
you'll notice long pauses and then suddenly everything I'm typing
will come spewing out onto your screens. :)"
Paleran ahs, "Kinda like me the other day :)"
Belegril grins.
Lukthil says, "Like for example I just got Gandy's statement. :P"
Sabrina patpats Luk
Mithdae chuckles.
Lukthil says, ":goes on: 3) We need a whole BOOK written about how to
_avoid_ string processing when possible. :) As a long time Turbo
Pascal programmer, I have got into this habit already. But I am
sure my understanding of how MUSH code handles strings is not as
good as my understanding of TP, so I guess I could benefit from a
good summary as much as most others. Of course, since MUSH code is
interpreted, there are TWO issues: parsing of the code, and the
actual execution. I know that curly braces and even square brackets
need a function call to be stripped off, and that any "substitution"
that does use a pair of parentheses is faster than an equivalent
function that does. BUT... what about things like calls to
"subfunctions" <u(object/attribute)> under this new "speed
attributes" code of yours? Is it better now to try to make lots of
subfunctions that can be re-used with u() calls and & save DB space?"
Lukthil says, "Needless to say I'm pasting the questions. :)"
Paleran grins, "Gee, ya think?"
Lukthil says, "Answer 3: There seem to be several things sort of mashed
together in this question. :) First of all, avoiding string
processing on a mush is totally impossible. Almost EVERYTHING on the
mush is parsed as a string. Every result of every function is of
datatype string. That means no matter what you do with your code,
it's gonna primarily parse strings so don't worry about it. :) Now
as for "subfunctions," this is *the* classic design question of
space vs speed. My speed-attributes modification doesn't really
impact on this question all that much. A u() function call is rather
quick in itself. It's only a single function invocation and does
work equivalent (in most cases) to a get() and a pair of [] brackets.
So if it's just sitting there by itself and it's going to save you
typing and db space, go for it. The only place where it could be a
bad idea is if it's somewhere where it's going to get executed a
lot. Examples: In a zone, use, or listen lock. Inside a loop like
an iter() or @dolist. In these places you want to save time and
function invocations at all costs so directly inserting the code
without using u() calls is probably the better choice."
Paleran wonders real quick if anyone is /logging this?
Sabrina thinks Nolin is?
Lukthil says, "I'm logging it."
Sabrina cools.
Paleran phews, "Cause I'm missing some of it :) Thanks Luk."
Thranduil phewwwwwwssss...
Nolindil is!
Belegril cheers. :)
Gandalf says, "When are we getting inline ufuns?"
Gandalf winks
Thranduil grins and does too
Lukthil says, "This brings up an important point that I want to highlight:
Zone locks, Listen locks, and Use Locks are BAD NEWS. They get
evaluated *all the time*. I'll say more about this later."
Nolindil laughs. "I have a question almost like that to add later, Gandy..."
Arlo has arrived.
Lukthil says, "Oh cool. Next question leads right into the lock issue. Here
it is..."
Lukthil says, "4) How about locks? I'm sure that attribute/string testing
locks (Species: or Culture:) use a lot of CPU. Is there some way to
optimize these? (Come to think of it "short circuit" evaluation of
these would be good too! Why _keep_testing if you know the lock has
been passed. I hope the MUSH is smart enough to quit when it knows
the result?)"
Lukthil says, "Answer 4: Yes the MUSH lock evaluation code is pretty smart.
It will stop early if it can. Don't worry about attribute-testing
locks. Those are rather fast. The locks you need to to worry about
are function locks of the form func/result. Those things can get
real nasty real quick. But worst of all are zone, use, or listen
locks that are function locks. Consider that the zone lock on ZMO
objects is evaluated EVERY time someone comes in contact in any way
with anything zoned to that ZMO. Just being in a room with an object
means the code needs to figure out whether you should see the
object's dbref listed along side its name in the contents listing
or not. And to figure that out, it needs to evaluate the zone lock
on the object's ZMO to see if you pass. Use and Listen locks are
almost as bad. Anytime anyone says anything near the object, those
locks are evaluated. So never never put function type locks on those
locks if at all possible."
Paleran says, "Can we comment freely?"
Lukthil says, "Sure go ahead."
Gandalf says, "What about Indirect locks?"
Paleran says, "Doesn't the MUSH know enough to stop its evaluation when it
hits the first match in its evaluation order? So when something is
said, it isn't checked to any objects (command wise), but when a
global is typed, each object nearby() is checked first before the
master Room is ultimately checkly last?"
Nolindil says, "A zonelock of Species:*Quendi* is less laggy that is_elf/1
where is_elf is strmatch(get(%#/species),*Quendi)?"
Lukthil says, "Indirect locks aren't much of an issue. The real work is done
when it hits the end of the indirect links and tries to evaluate the
meat of it. If the indirect links end in a function lock, then you're
hosed as usual of course. :)"
Sabrina erps. Indirect lock?
Donovan has left.
Lukthil says, "Paleran: Yes evaluation order saves you sometimes, but
remember that multiple matches are allowed on the same level of
evaluation. So it doesn't actually save you very often."
Gandalf says, "When you @lock foo=@bar so that foo uses the same lock as bar."
Lukthil says, "Nolindil: Definitely yes."
Sabrina ahhhs, okay. Thanks Gandy :)
Nolindil hmms...
Lukthil says, "Incidentally, I'd like to add a functionality for indirect
locks like @lock foo=@bar/enter sometime so be on the lookout for that."
Lukthil says, "Currently indirects only use Basic lock."
Gandalf says, "Very cool... I was annoyed by that."
Sabrina says, "So I guess 'tis much better to have a long string of people
who can use something than a function saying .. right?"
Sabrina says, "Nevermind, of course :)"
Paleran says, "Think before you type, Rina ;)"
Sabrina pokes.
Lukthil says, "Sabrina, yes it's better to have a lock like
=person|=person|=person than function/1
&function =[inlist(person person person,%#)].
I see this done a lot and it's a real lagger."
Lukthil says, "The usual solution is to create a little command on your zone
that redoes the zonelock when you add someone or remove someone."
Sabrina makes a few quiet adjustments. :)
Nolindil likes the function that Lukthil made for us.... :)
Paleran says, "Whassat, Nolin?"
Belegril says, "But that little command will just redone the lock, as you
say. Why not just paste another dbref to the list? :)"
Lukthil says, "This brings up a concept that is extremely valuable in
gauging the efficiency of a piece of code and making design
decisions of this sort but it is hard to describe in words..."
Nolindil says, "A command to relock our ZMO effiiciently."
Lukthil says, "Belegril: The lock is changed less times than it is
evaluated. So making a piece of code that takes more time to change
the lock is MUCH more desireable then making the code do that work
every time the lock gets evaluated."
Paleran ohs, "Pretty easy :) I don't mind typing it as I've got cut-n-paste."
Lukthil says, "I'm going to skip to question six because it highlights the
point I'm trying to get across..."
Lukthil says, "6) How about "audible" exits, rooms, objects, and the
filter/infilter attributes. Aren't these also a BIG drain of CPU?
Can these be optimized? Are there alternative ways to transmit sound
where necessary that is less of a load on the MUSH?"
Lukthil says, "Answer 6: Audible things aren't too bad for the mush to
handle. The MUSH propagates messages all over the place all the
time so a couple more aren't going to hurt too much. Filter/infilter
attributes are somewhat more of a concern. They are slightly more
painful CPU-wise than the Listen and Use locks I mentioned earlier.
Use them sparingly and if at all possible don't put functions in
them. In looking for alternatives, you need to ask yourself whether
the amount of work your alternative solution does is really less
than the audible/inprefix/outprefix. If you code some kind of big
@pemit that does exactly the same job, it's going to run slower
than an audible/inprefix/outprefix. The mush has to read and figure
out your @pemit code and then end up doing the same work it would
have done before. In general, if you have two ways of telling the
mush to do precisely the same thing, the way that places more of the
work in the hands of the hardcode will run faster. It takes time to
read your instructions so if you have a succinct way of getting the
message across, it's usually better."
From Sabrina, Pheona trots along quietly.
Sabrina smiles innocently.
Lukthil says, "This bit about sending the mush SUCCINCT instructions on how
to do something is true for every interpreted language. It's also
the rule of thumb I often use when *figuring out* the things I'm
telling you now. Basically the reason this is so is because I (and
others) sit around and use every expert coding trick we know to make
the hardcode handle things really fast. So once it figures out what
you want it to do, it will probably choose the fastest possible way
to get it done."
Lukthil says, "Often the biggest lag from evaluating code is figuring out
what the heck you want it to do and not actually doing it. :) So
telling the mush what to do in as short a form as possible is a good
way to go when looking for efficiency. :)"
Paleran looks up SUCCINCT in his online dictionary.
Thranduil looks over Pal's shoulder
Lukthil says, "Succinct - short and to the point."
Sabrina laughs
Thranduil nods,
Paleran okays, "Makes sense :)"
Belegril has a piece of code it take me myself a lot of time to figure what
the heck I want it to do. :)
Nolindil says, "Avoiding unnecessaty {} and [] is a major point of
succintness, isn't it? :)"
Lukthil says, "Example: pemit(person person person/list,message) is more
succinct than iter(person person person,pemit(##,message)) even
though exactly the same thing is done and exactly the same work is
performed. Yet the first executes much faster than the second."
Gandalf grins as he was considering making that point exactly.
Paleran nods, "The parser has to strip and reattach each set of [], so
that's two more operations that are most of the time unnecessary :)"
Sabrina gulps and looks longingly at her {}'s before pushing them away to a
dark corner on a high shelf.
Belegril has 10 pairs of {} in all of his 100+Kbytes code. :)
Krutt has arrived.
Paleran says, "They're necessary for separating code, sometimes."
Lukthil says, "Well the issue with {} and [] is a variant of what I'm
talking about yes. In that case it is simply confusing the matter
for the parser to use them where not necessary. It takes the parser
longer to figure out what is going on because it has to strip them
out and then make a special attempt to evaluate what's inside when
maybe it would have evaluated it anyways."
Belegril says, "Very rarely, in fact."
Nolindil says, "Using hard code functions whenever possible, staying close
to the hardcode, as it were, is the other point."
Nolindil stripped 3 pairs of {} out of his corkboard parent a couple weeks
ago & it still runs fine. :)
Gandalf wonders if that is his bastard corkboard he wrote so long ago.
Lukthil says, "Also, and this is probably surprising to someone who doesn't
know the hardcode and doesn't know language parsing theory, the \
escape operator is faster than the % escape and faster than {}
escape."
Belegril raises a hand.
Nolindil says, "You would not recognize it, Gandy. EVERYTHING happens in one
queue cycle now, powered by @pe/s, no @swi or @sel or any of that.
All functions. :)"
Lukthil says, "Belegril?"
Nolindil says, "Lots of fancy admin functions have been added, too."
Belegril had exactly the same question in mind, "I have most of attributes,
sometimes 30 lines long, organized as a single command with a lot of
side-effect functions. Is it better than using a number of ;
separated commands?
Lukthil says, "Actually Frodo sent me a question about that and so if you'll
wait, I'll answer you both in my answer to that one when we get there. :)"
Belegril says, "OKie."
Nolindil sent in 8 questions total... :)
Lukthil says, "Let me momentarily backtrack to question 5 which is really a
hardcode issue, but perhaps of some interest..."
Belegril says, "OK, I guess I had to login on time. :)"
Paleran grins and envisions Lukthil's display being questions on one side,
answers on the other and the MUSH window in the middle :)
Lukthil says, "5) Big, messy, technical question: Is there some way to add
MORE flags to objects on this MUSH? If we could somehow have FLAGS
(bits) instead of attributes for many character/object qualities if
would cut out a lot of string processing. Can the numbers 0-9 be
used for some "user defined bitfield" on objects? It would be nice
if there were Local Admin and +ruler flags so the list on #10000
didn't have to be checked for so many commands."
Gandalf says, "Wish people would stop mailing me asking me to show them the
code for objects I haven't seen or coded in over a year."
Nolindil laughs.
Sabrina laughs.
Paleran hehs, "If you were more like Lukthil and not show anyone your code,
you wouldn't be asked :)"
Lukthil says, "Just what I was about to say. :)"
Sabrina laughs!
Belegril chuckles.
Nolindil heehees... "I can remove you from the credits, Gandy. That way no
one will ask you..."
Gandalf says, "I always say no but people send them to me."
Thranduil glances at Gandy and grins.
Lukthil says, "Answer 5: There's certainly a way to add more flags but not
one that we would want to do. Flags are stored internally as a large
bit field. That is, every object gets two words of memory in which
are stored flags. One of those words are for "notype" flags (flags
that are independent of the object type -- room, exit, player,
thing). The other word is for type-specific flags. If we wanted to
add more room for flags, it would require adding another word of
space to every object on the MUSH. And due to certain programming
restrictions, we'd actually have to add two more words not just one.
Such a thing might be possible but we really don't want to do it
unless we have to. It would both increase the size of the database
and also increase the size of our runtime mush process for compiler
reasons that I don't want to go into here."
Gandalf says, "Just don't tell people you won't show them them code without
my permission. If it isn't my code I don't see the big deal..:) Also
lord knows I wrote it in less than an hour and at best it is an
excercise in how easy it is for anyone to write a database program."
Paleran says, "Isn't there a limit as to how many flags you can have on a
MUSH anyway? Like FORCE_WHITE is 0x8000000 or something huge."
Sabrina pokes Gandy cos she doubts she could do something like that without
a great deal of work. :)
Lukthil says, "Well that would be the limitation of the size of a word in
memory. Words are two bytes - 16 bits long."
Lukthil says, "So to add more, we would have to add two more words to the
size of that struct field and we could have higher-valued flags."
Sabrina timidly raises a hand.
Lukthil says, "Yes, Rina? :)"
Nolindil says, "I didn't tell anyone that. Someone misrepresented me. :-/
I just don't want to give my code to someone who wants to take it
to another MUSH."
Paleran is hazy on those memory things, but this isn't a class for
hardcode :)
Lukthil places a straw in the jar on his desk.
Sabrina umms, "This is likely to sound really dense, but how much of a
difference does it make as to the number of flags a person has
set? For instance, look at my line of flags. Obviously, my chown_ok
flag serves no purpose, but does it take up that much space by my
having it set? :)"
Lukthil says, "No, that's a good question. It doesn't take up any extra room
or extra time. That's because the space for all those flags is
essentially allocated whehter or not they are set. That's how
flags work."
Sabrina cools and remains chown_ok then! :)
Belegril still doesn't know what flag 'l' would stand for. :)
Lukthil says, "And that's also why we wouldn't want to add more flag
options. Because every object would need the space for those
flags allocated on them even if they were unused."
Sabrina says, "That is 'light', Bele. It means you show up in the contents
of a dark room."
Belegril arrgs, "Don't visit my ship with such a nasty flag. :)
Nolindil laughs, "I think he was referring to 0-9"
Thranduil grins.
Sabrina cackles
Lukthil says, "7) As a ZMO programming LA, I'd like to know a little about
command parsing. Is it more efficient to use ONE command with a *
after it for multiple options: viz -
$elfwho/*:@sel %0=<letter1>,action,<letter2>,action,etc.
OR: have elfwho/m and elfwho/n and etc. as seperate commands calling
a function attribute with u()?"
Gandalf says, "Ew.. I was curious about this myself."
Sabrina guesses the first? :)
Paleran hrms and could think of a few BS answer, but will wait for a real one :)
Lukthil says, "Answer 7: For the most part, there is not much of a difference
between the two choices as far as CPU goes. One thing you may need
to think about is that making one command with many options often
involves a bunch of extra code to figure out what option was
specified. If that code becomes unwieldly, you may wish to separate
it into separate commands. Personally, the way I do things is that
if the various options share any code, I make them one command.
Otherwise I split them up."
Sabrina chuckles and guesses wrong. :)
Lukthil says, "So essentially it is mostly a database-savings problem. CPU
is relatively unaffected unless you have gobs of extra code to parse
the options."
Paleran says, "It doesn't mater that its a zone command that would most
likely get used alot?"
Sabrina says, "gobs .. now there is a word ya don't hear too often :)"
Belegril notes that gob also means sailor. :)
Lukthil says, "Not really, Paleran. The $command scanning routine is quite
fast and a single extra attribute on the object doesn't really
matter. There have been a few rare cases where this became an issue
but they are fairly rare. For example at one time in the mush's
history, the +com database object sat in the master room. This was
a rather bad thing because in the case of a big database object,
you start getting a LOT of attributes to check for $commands. When
it gets that bad, it starts making a difference."
Nolindil gets back to the inline ufun() that Gandy winked about. "OK, how
about putting CODE in a register & calling it? Is this possible &
if so, worthwhile? I.e. setq(5,get(me/big_function))
iter(<list>,<set data in other registers>switch(<test>,yes,%q5,etc...))
-- Does that save the ufun() overhead somewhat? Would code called in
the way properly evaluate %0, %1, etc, and the other q registers?
Lukthil says, "Well first of all, Nolindil, what you gave there wouldn't
exactly work. The %q5 would evaluate out into the body of the
programming and that programming wouldn't get evaluated. However,
s(%q5) would probably do the trick. It is doubtful however that this
would save you much. First of all you've got just as many or more
function invocations (at least one s() for every u()). Secondly the
only thing you are saving is one less attribute lookup IF the %q5 is
used in more than one place on a given run of the code. That's not
very much especially considering how fast lookups go now."
Nolindil If you need the s() then I can see how it doesn't save anything.
Oh, well.
Lukthil says, "That's it for Nolindil's set of questions. Frodo has a few
and I typed out the answer to the first one during the save..."
Lukthil says, "1. Cloning vs. Parenting: I've always assumed that parenting
was A Good Thing. And that any contribution to lag caused by
parenting was negligible, and completely swamped by the space-saving.
Is this in fact correct? I've heard people say that parenting should
be avoided in favor of cloning since it causes lag, but I assume
this is totally false."
Lukthil says, "Answer 1: You're instinct is correct. Any lag caused by
parenting an object is completely inconsequential compared to the
db savings. Parenting is definitely preferred to @cloning where
possible. The only time this could ever be a problem is if either
the parent object was incredibly huge but if it were split into
cloned objects, each cloned object would not be huge (you'd have
to have some pretty bizarre code for that...) or if you had some
kind of chain of parented objects which had up to like 10 or 15
levels of parents. I've never seen anything like that so I don't
think that's a concern."
Belegril has a parent in third degree. :)
Nolindil eeps
Sabrina thought everyone had parents.
Lukthil says, "I've had up to about 5 but never anything like 10. And if
you keep the size of each object small, it's not much of a big deal
anyways."
Belegril chuckles, "Coded, not biological." :)
Sabrina says, "Hmm ... what're you meaning by levels of parents, Luk? :)"
Lukthil says, "No I'm talking about my biological parents. Computers breed
in sets of 5 to 10."
Sabrina giggles.
Thranduil laughs.
Nolindil laughs. "You didn't know Luk is an AI???"
Belegril says, "The second one is small. The first one is big. The third
one is just the help object. :)"
Belegril LOL!
Sabrina says, "an Al?"
Nolindil says, "The third one is unnecessary then. Just hard code the help
to reference that object. It will be faster.""
Lukthil says, "Using help objects via parenting isn't usually a wise
decision. Better to read the needed attrs off the help database
object directly without parenting."
Belegril knows nothing of hardcode but the word itself. :)
Paleran whispers to Rina, "Artificial Intellignence."
Nolindil does that with his corkboard parent. Help is larger than the code. :)
Belegril says, "You mean putting code on the object to read from the help?"
Lukthil says, "Whattayamean Artificial?"
Sabrina ohs! AI, not Al - stupid Mac :p
Paleran grins, "Never call a Mac stupid ;)"
Thranduil sits on Rina.
Lukthil nods to Belegril.
Belegril says, "Well, that's right. Thatr would take only two $-commands."
Paleran says, "Call them downsizing, but not stupid :)"
Sabrina oomphs and is squished, but still thinks the Mac is not very smart.
Nolindil says, "Yes, put the DBref of the help object in the code."
Nolindil lags...
Lukthil, for the record, thinks Macs are deceitful.
Sabrina squishedly -still- doesn't understand what you're referring to as a
level. :)
Thranduil hrmsssss
Paleran grins and puts his hand over Rina's mouth, "Let's not discuss that
here."
Krutt thinks Lukthil is very wise.
Lukthil says, "Rina: @parent object1=object2; @parent object2=object3."
Sabrina bites Pal's hand hard!
Lukthil says, "Object1 has two levels of parents."
Paleran has metal gloves on :)
Belegril hates putting right-away references to dbrefs in the middle of
code. I'd better have &helpobject=<dbref> and go with v(helpobject)
Sabrina ahhhs and smiles, 'Thanks ... just wanted to make sure :)'
Belegril says, "You nuke it, you recreate another one from @decompile file,
the dbref changes."
Nolindil lags some more...
Krutt has a question.
Lukthil says, "Belegril, one little trick I use is to do @va object=#xxxx to
denote my reference objects. And whattaya know that's Frodo's
question #3... I'll skip to it."
Belegril says, "Go, look for all the instances you use dbref then."
Halbarad says, "Use va, vb instead, Belegril. Then call them via %va, %vb,
etc..."
Belegril says, "Oh, right."
Halbarad dohs and types slow.
Lukthil says, "3. Is there any advantage to using @VA rather than &foo
attribs?"
Belegril says, "I always forget about them. I'd forget what they mean since
I have lotsa attributes."
Lukthil says, "Answer 3: There is one advantage to using those attributes.
The substitution %va evaluates noticeably faster than the v(va)
lookup. That's the only thing. Everything else about them takes the
same amount of time."
Paleran thinks of a question, but will wait.
Krutt raises his hand.
Lukthil says, "Krutt?"
Belegril says, "But cannot one forget what each attribute means if you
store them under such names? :)"
Nolindil says, "That's what documentation is for..."
Lukthil says, "If they only have dbrefs in them, it's pretty easy to figure
out, Bel. Just look at the object. :)"
Belegril grins.
Krutt hrms, "Okay, I am not sure if this is possible, but could you do
something like @parent object1=object2 and then
@parent object2=object1? Would that cause any problems?"
Galdor says, "Object cell-division, neat."
Belegril acks.
Sabrina says, "hmm...can't think of why you would wanna..."
Lukthil says, "No, you shouldn't be able to do recursive parenting. The
@parent command checks up the parent chain to make sure you aren't
making a loop. Also if it ever did happen somehow, the game would
catch it. It won't iterate up past 15 levels of parents."
Sabrina laughs
Nolindil eeps!
Nolindil says, "Infinite loop if the MUSH doesn't disallow it.."
Paleran thinks that would fall under the Oedipal complex division.
Lukthil says, "It's got recursion checking, Nol. :) Same as all the other
user-definable loop constructs in the code. :)"
Galdor says, "Object cancer.."
Krutt hrms, "Well, I was basically thinking of making the two objects
'friend' if you know what I mean."
Paleran says, "Lag bug is catchy, tonight."
Nolindil says, "Or the Congressional Budget Algorithm..."
Thranduil grins.
Belegril says, "Then why parenting them recursively? :)"
Lukthil says, "What you should do, Krutt, is place the code on one object
and then parent both of them to that one."
Sabrina glares at the Guard she's carrying around and says, "Alright, buddy.
You're undergoing some major changes!"
Belegril says, "Oh use one of them as a parent."
Krutt nods, "Well, I guess I will... but it would be nice to do it the other
way..."
Lukthil says, "Nice if it were possible and it didn't go recurse crazy and
check 15 levels of parents every time anything was accessed. :)"
Nolindil says, "If the child needs to transmit info back to the parent, you
need to use some "common data object""
Lukthil says, "Here's a simple one: 2. What about parenting exits and rooms?
We have a bunch o' exits parented to Master Front Door (#16381),
which supplies the basic description and messages. Is this to be
(a) encouraged as space-saving, or (b) discouraged as laggy?"
Lukthil says, "Answer 2: I'll take (a) for 200 points, Alex. Parent away. :)"
Sabrina laughs
Belegril chuckles.
Nolindil laughs
Lukthil says, "4. What are the relative costs, in execution time, for
setq(), &temp me=, and @set me=temp:...?"
Paleran says, "You didn't phrase your answer in the form of a question."
Lukthil says, "Answer 4: setq beats an attribute set of any type every time.
&temp me= and @set me=temp: are equivalent, however often you may
save something by using &temp me= because it will cost you less []
brackets. Example: &temp me=add(1,1) is faster than
@set me=temp:[add(1,1)]"
Lukthil says, "The case of set() vs. &attrib is another matter entirely.
That falls under the function vs command debate which is coming to
an auditorium near you soon..."
Lukthil says, "5. Just how expensive is it to enqueue and dequeue commands?
For example... (a)
@swi foo=bar,do-something-complicated,do-something-else
versus... (b)
@swi foo=bar,@tr something-complicated,@tr something-else.
The latter is often easier to code. There obviously is SOME extra
cost in (b), but is the queueing time swamped by the other stuff
that is going on?"
Belegril says, "Ack. I've heard that one can omit [ after ( and after
commas. But after =???"
Sabrina says, "sometimes after = ... depends Bele"
Lukthil says, "Answer 5: Queuing time is highly dependant upon what else is
going on on the mush. Using more queue cycles is like driving in
the slow lane. Anybody who wants to pass you can do so. Our mush is
active enough so that choosing to use more queue passes usually
results in a noticeable lag in your code. However, the nicer part
is that although your code lags, it doesn't lag anyone else.
Everyone else goes zipping by in a single queue cycle while yours
waits around and uses several. In your example, the @swi takes one
queue cycle and the @tr takes another. That's a total of 3 (counting
the original queue) whereas a purely functional approach would take
only 1. In general, you should probably stay away from the
multiple-queue cycle style code for faster code."
Lukthil says, "And that brings us to the function vs command debate."
Paleran loves this debate :)
Lukthil says, "Unlike commands, functions are evaluated _synchronously_.
They do not use the queue. They all get evaluated all one at a time
and everyone waits until they are all done. That means they make
your code run faster although they make other people go slightly
slower. In the end, however, the net gain of the entire mush is
that functions end up being faster in total."
Lukthil says, "However, there are a couple other matters to be considered
here..."
Thraile has arrived.
Lukthil says, "The first of these is probably best illustrated by an example
of mine: The +mail +clear command..."
Paleran grins to Rina.
Nolindil hmms. "I hadn't considered it that way. But that's what Queue is
all about... controlling the rate at which stuff executes. So, are
there situations where you _ought_ to use more queue cycles to slow
you code & not lag everyone else? Or is that an issue for Wizards
only?
Mithdae has disconnected.
Elree has arrived.
Sabrina wonders what she did this time.
Mithdae heads back to Middle-earth.
Elree says, "Hello"
Lukthil says, "The +clear command which wipes out all your messages marked
for deletion uses a big @dolist to clear the messages. That means it
queues a command for every message instead of doing it all in a big
iter(). Why did I decide to do it this way? Two reasons:"
Sabrina wishes Luk hadn't said +mail ;_
Sabrina ;)
Paleran knows one :)
Frodo raises his hand, "To be nice to other users and let them do things."
Lukthil says, "First: Getting the job done when clearing mail and not
getting messed up and aborting in the middle is very important.
Otherwise we end up with lots of mail messages in the mail database
that have no owner and the mail database gets bigger and bigger and
bigger... Why is this a concern? Because if I had used iter(), this
could potentially happen and here's how:"
Belegril says, "Invocation limit?"
Lukthil says, "As many of you probably know, there is a "function invocation
limit". You can see what it is set to by typing @config and reading
the entrees until you find it. On our mush it is set to 3500. That
means a single command is allowed to make 3500 function calls and no
more. After that, you just get an error message and the functions
don't evaluate and your code dies. If someone deleted enough
messages at once, this could happen and then those messages wouldn't
get deleted properly."
Elree says, "I was wondering how to get rid of my messages...now i know"
Belegril used to pas that limit. :)
Belegril says, "pass even"
Paleran grins, "Ok, so I know the other reason, then :)"
Thraile raises a hand, "But wouldn't you have to have lots and lots of mail
to do that?
Belegril says, "Eonwe's straw target dummy in the arena always does. :)"
Galdor grins at Thraile, "Uhm.. yes :)"
Sabrina laughs, "Thraile, some of us -get- lots and lots of mail in a week!"
Lukthil says, "Second: This is what Frodo was saying. +clearing messages
takes time. If I made it an iter() then everyone on the mush would
have to just stop and wait while it did all that stuff. This would
be rather painful. So in this case I actually WANT to take the
option of wasting more time. We can afford to take time to delete
mail. Nobody is sitting around examining the mail database object
pining for all the messages to be gone. So we let other stuff
execute faster while it works in the background."
Paleran points to Luk, he probably gets around 50 messages a day :)
Nolindil has frequently passed the limit... :)
Sabrina has gone through no less than 20 already today :p
Nolindil wonders if the rumor that Wizards ger priority in the Queue is true?
Lukthil says, "It is not entirely uncommon for admins to have up to 400 mail
messages in their boxes at times."
Thranduil gulps
Lukthil says, "Wizards do not get priority in the queue unless they use the
@kick command to force it through. That happens from time to time
when necessary."
Sabrina says, "so -that's- why y'all warn us before +clearing ;)"
Paleran ohs, "I know a third then, Luk...One you told me before....There
is a check to see if noone is left to own the message, and if
something gets parsed before it, or during it, the cycle could
get messed up, and not get deleted at all, or deleted accidentally."
Lukthil says, "I believe the current record is held by the now retired
Kerowyn who had 600 messages in her box. It actually broke the mail
system and I had to delete them by hand."
Sabrina yowsers.
Thranduil ick..
Thraile chuckles, "wow"
Galdor says, "A running joke among the admin. She just never clears her
mail :P"
Nolindil laughs!
Galdor says, "We try to remind her every time she logs in."
Belegril chuckles.
Lukthil says, "Since then I have made the system a little more robust and I
don't know the upper limit anymore. It's higher than 600."
Sabrina has never had more than 20 at one time ... I think. I read, /log,
+reply, and +delete immediately usually :)
Nolindil says, "Maybe automated expiry is needed. :)"
Thranduil blinks at Nol. "Naww!
Sabrina says, "NO! If I -do- save something, it is saved for a reason."
Lukthil says, "I don't want to get into that here, Nolindil. That'd be a
very difficult problem to code."
Paleran says, "It wouldn't be a matter of porting the BB code?"
Lukthil says, "Not nearly so easy. There's a lot more players than there
are BBoard. I'd surpass any function invocation or queue limit that
exists in trying to code it."
Sabrina grins, "Although ... while we're on the subject of BBs Luk ...
Lukthil says, "There was something else you mentioned, Pal.. about breaking
a cycle or something?"
Paleran nods....Thought it had scrolled by and didn't want to mention it
again :)
Halbarad heads back to Middle-earth.
Halbarad has left.
Lukthil says, "The check that you refer to is there, but I don't see how it
relates to the command vs function question."
Elree has left.
Paleran says, "If it was a function call, one queue cycle may take less
time than another."
Belegril raises a hand.
Paleran is probably confused :P
Lukthil says, "I don't see why that would matter."
Thranduilmirk :laughs and laughs.
Thranduil eeeek..
Paleran doesn't want to bring up specific exapmles, "But when I coded a
mail system, I had a message get to -1 once. I had assumed it was
due to that :)"
Sabrina chuckles
Lukthil says, "Sabrina, you had a question? I'll get you next, Bel."
Paleran says, "-1 for the number of people it thought owned the message still"
Lukthil says, "I haven't had any problems with that, Pal. I did have a
problem with what happens when the mush crashed in the middle of
deleting a message, but I've fixed it now so that it can detect the
crash upon the next startup and repair itself."
Sabrina grins at Luk and nods, "Is there any way of upping the number of
messages displayed when doing a +skim on the BBs? We have a 10 day
timeout on Bad-bb and very often run over 50 messages (right now
we're in a slump with only 31) and it is very easy to miss them if
they're not displayed when doing a +skim. :)"
Thraile says, "Hey, could you fix my e-mailer? when It crashes, it messes
up too ;)"
Thranduil says, "BB and +mail...50 messages tops on +skim :P"
Lukthil says, "No, there's no way to up that limit because 1. it would
mean too many function invocations and everyone would lag and 2.
it could overrun the string limit. You can +skim ranges to see the
rest of the messages."
Lukthil says, "E.g. +skim public 50-100"
Sabrina says, "how about possibly giving a message at the END of the +skim
to show how many more are there instead of the beginning? :)"
Sabrina sits back down and shuts up again. :)
Lukthil says, "That would be easy to do. Although this isn't really the
place to be discussing this. :)"
Lukthil says, "Bel, you had a question?"
Sabrina grins and nods, and is why she sat back down. ;)
Belegril says, "Several."
Lukthil says, "Fire away."
Paleran will BRB>
Belegril says, "First, why we understand about quequeing vs. immediate
execution, wouldn't a function call be still faster than a @-command?"
Belegril says, "Er, why=while"
Lukthil says, "Yes it would. As I mentioned, the net gain across the entire
mush is that a function will execute faster than a @-command. If
that's the only issue to be examined, then your choice should be
to use a function."
Lukthil says, "More specifically, although the actual work of the @command
is the same as the actual work of the function, the overhead of
parsing the function is less."
Belegril says, "Second, how laggy is the dawn/dusk triggering? Does it just
involve adding the object's dbref to some list or it involves some
more complicated routine, too?"
Lukthil says, "The dawn/dusk triggering isn't anything hardcode related. It
is controlled exclusively by softcode. We have a system clock which
simply @waits in the queue (that's the wait you always see in there)
and when it hits a certain time, it @dolist-@triggers a list of
dbrefs. Naturally this is a laggy thing so we try to keep the list
small. However it is intentionally done this way so that other
things can execute faster while this one runs more in the background."
Belegril says, "Third question is related to the second one. Is it better
to have a long iter() on one object triggered at dawn/dusk or a
number of small things on a larger list of triggered objects?"
Nolindil says, "Is there anything like a CRON built into the MUSH? Or is
the lack of this the reason for the dawn/dusk trigger?"
Lukthil says, "In general, there is almost always a way to code things
without having to depend on a cron style trigger like this one.
The method is called "lazy evaluation" or as I call it "quantum
programming"."
Sabrina says, "can't it be hardcoded? :)"
Lukthil says, "Belegril first..."
Belegril says, "What's 'cron' style?"
Paleran has reconnected.
Paleran has partially disconnected.
Paleran heads back to Middle-earth.
Paleran has left.
Paleran has arrived.
Lukthil says, "It depends on the size of the iter and how intensive it is
really. Best thing is to not have anything at all triggered at
dawn/dusk if possible."
Paleran wishes it woiuldn't do that for partial disconnects :P
Belegril repeats in case you missed it, "The third question is related
to the second one. Is it better to have a long iter() on one object
triggered at dawn/dusk or a number of small things on a larger list
of triggered objects?
Belegril says, "Oops, sorry."
Lukthil says, "Nol: There's no such thing as a cron in PennMUSH. Sab: It
could be hardcoded, but I haven't because there is actually little
need. The hardcode would be insignificantly faster than our softcode
version."
Belegril says, "What are the alternatives, then?"
Sabrina nodnods
Paleran says, "ONe at a time people, heh :) Luk may be an AI, but he's a
sensitive AI."
Lukthil says, "Bel: A "cron" is a name borrowed from unix systems for a
system clock that sits and runs in the background and triggers
processes at specified times."
Belegril says, "Oh, I see."
Lukthil says, "Alright... alternatives... This will take some explanation
but it is a major programming principle so it's good to know."
Lukthil says, "Lazy Evaluation or Quantum Programming: The basic idea of
this is as follows: Why do something if there's nobody there to see
it? (E.g. if a tree falls in a forest and there's nobody there to
hear it, who cares if it makes a noise.)"
Belegril chuckles.
Paleran says, "Or does it make a noise?"
Lukthil says, "Most timed activities are pointless. Most often they run and
nobody is around to see it happen. Almost anything that ever gets
done on the mush is somehow in response to something that a player
does."
Lukthil says, "Paleran: No the important part of this is that we don't care
if it makes a noise or not."
Belegril says, "My fourth question then is what about that BYOC (Bring YOur
Own Code) subtitle for the class? :)"
Paleran cowers, "It was a joke, sorry :)"
Sabrina laughs
Lukthil says, "Just a sec, Bel. We'll get to the code stuff if/when there
are no more questions. :)"
Lukthil says, "Let me explain this lazy evaluation thing..."
Nolindil says, "It looks like we have reached that stage, Bel. Time for
examples. :)""
Belegril has got a huge one. :)
Frodo has left.
Lukthil says, "Let's take the example of a shop door that needs to be
unlocked during business hours and locked during business hours.
This is one I've seen often."
Sabrina hides her guard and all her other code and goes back to her seat.
Sabrina says, "and locked during night hours, you mean? ;)"
Lukthil says, "Some people insisted that they needed a cron trigger for
this. But let's ask ourselves... what is the point of
locking/unlocking the door if nobody's trying to go in there? No
point whatsoever. Then WHY spend all our time doing this? :)"
Lukthil says, "Right sorry. Locked during night hours."
Belegril says, "switch(u(#4561/day.night) would work"
Belegril says, "In @lock on the exit."
Lukthil says, "Exactly, Bel. First think about what action exactly we are
responding to. Then code the thing so that it quick fixes itself up
when this happens."
Lukthil says, "We are being lazy about when we evaluate things. Sure we
should have been locked this whole time, but who's to know? When we
get awakened by someone trying to get in, we quick fix it as if it's
been locked or unlocked the entire time and nobody's the wiser."
Lukthil says, "This idea can be extended to much more complex situations."
Vinyesse has reconnected.
Belegril says, "Umm, I have an example when I might really need cron trigger.
But it's got a tripple nested iter() and I dunno how to get rid of it."
Paleran ouches, "That's gotta go :)"
Lukthil says, "There's one last question by Frodo and after that we'll go on
to examples."
Belegril says, "OKie."
Lukthil says, "It's fitting that this should be the last question I got
because it's also the most insightful..."
Lukthil says, "6. At any given instant, what activity is the MUSH hardcode
most likely to be doing? Parsing something? checking the queue?
generating output?"
Galdor says, "Checking if Galdor's idle."
Nolindil laughs!
Belegril grins.
Galdor knows it does..
Thranduil laughs .
Sabrina hmmms at G.
Lukthil says, "Answer 6: That is a very very deep question and one which I
would very much like to know the answer to. And in fact it changes
daily as I make more and more improvements to the code. I could very
well be wrong about this, but right now my theory is that most of
the time it is either evaluating a function or queuing output to
someone. Probably evaluating a function. As I find out the answers
to this and other similar questions, I "attack" those pieces of the
hardcode in order to make them faster. That's how I improve our
speed on Elendor."
Nolindil says, "Answer: Lagging!"
Vinyesse has partially disconnected.
Vinyesse heads back to Middle-earth.
Vinyesse beams away.
Vinyesse has left.
Lukthil says, "Alright are there any further questions before we go on to
examples?"
Paleran nods...
Galdor has one, more of a pet peeve tho..
Nolindil says, "I would bet the parsing code is a major part of the MUSH
profile, too. And that's what we are here to improve..."
Krutt has one.
Lukthil says, "Paleran, you were first."
Paleran says, "A long way back we were talking about @parenting objects and
such...Now what about control passing via the @tr to another object?
Is it more efficient to keep code on one object, or separate the
code into a few objects to lessen parsing time for a major object?""
Paleran has coded that u() is of course more efficient either way, but 2tr
is differnt.
Lukthil says, "In terms of speed, the less @triggers the better. @trigger
won't save you any parsing time because in the end the mush needs to
do more work in order to parse the @trigger itself."
Belegril says, "@triggers are dammmmmmmmmmn sloooooooooooow............. :)"
Thranduil says, "Amen Bele.."
Paleran says, "Yeah, but sometimes necessary..."
Sabrina screams, "OK! I'll TAKE the @tr out already!" ;)
Lukthil says, "Also the less Rina's in your code, the better."
Belegril says, "Never. Unless you have to trigger the attribute inside
itself and it is not a long function. :)"
Sabrina pouts
Belegril chuckles.
Thranduil laughs!
Nolindil says, "They are useful for rarely executed commands, though. No
$ pattern to be searched all the time."
Lukthil nods to Nolindil. "Sometimes I want to execute Rina too."
Paleran says, "Yeah, can be space-saving and make code easier to read..."
Nolindil laughs
Sabrina really pouts!
Belegril chuckles.
Galdor says, "Would it a) be beyond my scope of power, b) would anyone mind,
if I campaigned for the removal of player @*tport messages and
@aconnect/@adisconnect spam @emits? These bug the heck outta me
(and the @*tports mess with my 12v ship code.)"
Paleran patpats Rina, "That's OK, he means well."
Lukthil says, "Pal, the space saved is usually rather inconsequential. In
general, code db space is inconsequential compared to space taken up
by texts."
Sabrina bites Pal's hand, "Lemmelone."
Nolindil doesn't like otport and oxtport either... can we _disable_ it on
certain rooms or something?
Belegril says, "And my ship code, too. :)"
Sabrina awwws at G, "And I just set my *port messages too!"
Galdor has wrestled with this issue before a bit.
Galdor says, "Nuke em, Rina."
Galdor :P
Thranduil nods and does really dislike those..
Paleran doesn't like them either...Or maybe they'd be better if you put a
limit on how long they can be.
Belegril says, "@adisconnect is useful for +douse and @elock me, though."
Sabrina takes her cookies and stomps off to a corner to sulk.
Galdor says, "Or just switched them for IC/!IC"
Nolindil says, "Or had a setting on rooms to prevent them working..."
Paleran says, "15 or something."
Belegril says, "Yeah, no IC @atpors!!!"
Belegril :)
Nolindil says, "OOC is when they are worse. 100 people in the OOC room for a
ceremony is a nightmare..."
Lukthil says, "Krutt, I think you were next."
Galdor says, "I dont mean to remove the 'code' of connect/disconnect, its
the ones like @adisconnect me=:jumps up and down and disappears ina
cloud of miniature unicorns flittering about his head. Or similar
crap/spam like that. Especially when people +set ooc real quick and
disco in an IC crowd."
Paleran says, "Could an otport be considered speaking somehow?"
Krutt nods, "Okay, I was wondering about @wait... back when you were talking
about locking a door... @wait would be a sloppy way to do it really
fast... but, would using the @wait cause any problems?"
Sifca has arrived.
Sabrina slips Thraile a chocolate chip cookie cos he's not being mean to her. :)
Nolindil grins.
Sabrina says, "@wait is very slow :)"
Belegril says, "Nolindil gave a good idea -- option for suppressing @atports
in aome rooms. My ship would be the first candidate. :)"
Nolindil says, "Does @wait cost money now? If so, how much?"
Paleran doesn't know what people have against @wait...When used right, it is
quite useful.
Lukthil says, "There's no reason to use a @wait at all in the case I spoke
of. You should lock the door with a function lock that evaluates the
time of day."
Thraile nods and accepts the cookie and pulls out a glass of milk
Azakhil has arrived.
Paleran says, "1/64, Nolin :)"
Lukthil says, "@wait doesn't cost any money although it requires a queue
deposit which is refunded after it gets out of the queue."
Lukthil says, "Yeah all commands cost the 1/64th."
Sabrina says, "Just that they do tend to be laggy, Pal."
Paleran shakes his head, "Not if used in moderation..."
Belegril says, "Verrrrrrrry lag, I surmise. :)"
Belegril says, "laggy even"
Belegril has a single @wait in all his 100+ Kbytes code. :)
Thranduil says, "Well...what ya expect, they are after all a _wait_ ..."
Lukthil says, "Sometimes the solution to lazy evaluation problems involves
queueing a @wait though. For example, an object listens for * has
arrived and queues a @wait in case something happens. If they leave
before it happens, the @wait is simply dequeued."
Paleran says, "Granted a @wait/@tr is the most lag you can get."
Nolindil says, "I heard someone complain that "@wait costs a whole bunch of
mone ynow" and wondered how much that might be..."
Nolindil says, "Have Queue deposits increased?"
Azakhil says, "What about @waiting on semaphores? are they as bad as @wait
<time>?"
Krutt nods, "So, doing something like @wait 14400=Do_this wouldn't really
bog down the queue?"
Belegril says, "Worse, I think."
Lukthil says, "@waiting on semaphores is less laggy."
Belegril says, "Oh, sorry."
Nolindil hehs
Paleran says, "Azakhil, no because the Semaphore queue is parsed differently
than the wait queue."
Sabrina says, "uhhh...yeah, it would Krutt"
Paleran looks to Lukthil for the answer to Krutt's...
Lukthil says, "It wouldn't all by itself, but we wouldn't like it because
it's a case of "if we let you, then we have to let everyone" and if
more than a few people did it, we'd have problems."
Belegril acks at Krutt.
Sifca says, "code waiting to be executed by @wait is loaded into the procces
and thus to disk yes?"
Lukthil says, "To disk, Sifca?"
Paleran thinks he means RAM ;)
Lukthil says, "To ram disk, if you want to look at it that way."
Sabrina says, "she ;)"
Sifca says, "The procces...Pagefile...swapfile...hrm, okay"
Lukthil says, "That's not a big deal though because it's just one buffer
long. There are buffers allocated all over the place all the time
in the hardcode."
Paleran gacks, "I should use functions for those type of things :)"
Lukthil says, "It's really inconsequential, Pal. The database is 30 megs
ram. Allocating 8000 bytes temporarily for a buffer isn't going to
do anything."
Galdor has left.
Nolindil says, "So, the _compressed_ DB takes 30 megs of RAM now? If it were
not compressed, how big would it be? :)"
Thraile has disconnected.
Thraile heads back to Middle-earth.
Lukthil says, "Well 30 megs is a guestimate. The entire process is 45 megs
in ram. Uncompressed on disk it's 42 meg database."
Nolindil wows... "42 million characters of text & code. We are quite an
elaborate world here."
Azakhil says, "I am curious, aside from the process of @waiting and
@notifying...would it be bad to @wait on a semaphore and just let
it sit there for several days? That is, does it lag much while just
sitting on the Semaphore queue?"
Thraile has arrived.
Lukthil says, "No, you wouldn't notice much lag at any given time but it
would be an annoying thing because it would slow down access to the
semaphore queue for the duration it was present."
Nolindil says, "If 50 different people did this, it would be a problem, I
bet..."
Lukthil says, "If we see stuff like that hanging around, we disable it.
Can't have it building up."
Azakhil says, "Ok, I see...not that I've ever seen it bigger than 3 for a
period of time :)"
Lukthil says, "That's because we keep it small on purpose in the long term.
Otherwise it'd build up as more and more people coded things that
make heavy long term use of it and then let them all run."
Azakhil says, "Well I have avoided doing it for fear of lagg or loosing it
in a crash..."
Sifca thanks lukthil for the info and excuses herself as she is being
summoned.
Sifca has left.
Lukthil says, "Yes, that's another thing. Crashes screw a lot of things up
which is why I try to get everyone to stop executing things before
I shutdown."
Paleran says, "@startup :)"
Lukthil says, "That's the coders solution, but how many of you have done
that for all your objects? :)"
Belegril grins.
Paleran has only needed it for two.
Paleran says, "Otherwise, it would be on all of them :)"
Azakhil doesn't need it
Belegril says, "What would I need @startup for?"
Lukthil says, "I have it on two as well."
Sabrina says, "@st...neverminds and looks at the help :)"
Paleran guesses +mail and +top :)
Lukthil says, "Good guesses. :)"
Paleran grins, "Wonder why ;)"
Azakhil says, "Thanks for the info, I was wondering about the efficiency of
semaphores....presently I only use it for mutual exclusion...
@waiting and @notifying once a command list is done..."
Paleran says, "Rina, simply it is a list of commands that are executed when
the game is brought up."
Paleran says, "When someone hits that 'restart' button :)"
Krutt has a question.
Lukthil says, "Krutt, go ahead."
Krutt hrms, "Well, this is probably a dumb question, but when a player
disconnects, is he set no_command, and what exactly happens to
the player?"
Lukthil says, "He is set no_command?"
Paleran says, "Be a neat feature, to say the least :)"
Sabrina says, "Nope, he isn't, Krutt :)"
Azakhil says, "No_command is set if the player/object has no $commands..."
Paleran says, "Everyone should be uselocked to themselves, anyhow."
Belegril says, "So, l"
Lukthil nods to Azakhil. "Yes and that is a recent change..."
Belegril says, "Argh."
Lukthil says, "When a player disconnects, his ADISCONNECT attribute is
executed if it exists, his connected flag gets unset, and he is
left as a regular old object in the room. The object doesn't appear
in the room contents listings. That's about it."
Krutt nods, "Well, I guess that wouldn't really matter, depending on what
happens to the player object... is it set invisible, or moved to a
holding room, or just removed completely?"
Lukthil says, "No, it just sits there. The connected flag gets unset and
the mush programming automatically screens non-connected player
objects from room contents listings."
Nolindil says, "LAST is also changed..."
Azakhil says, "No it's in it's location...if you control a room...you can
see the people in it, if any....regardless of the flag.."
Nolindil wonders if some other player_invisible attributes are also changed...
Paleran says, "That's part of the Master Room, Nolindil...+top, too I would
imagine :)"
Krutt nods, "Ah, Okay... but, does it still scan the player for $-commands?"
Thranduil says, "Is just a dark object..and if aint uselocked, can be
annoying dark object sometimes :P"
Lukthil says, "Yes, players are still scanned just like any other object."
Lukthil says, "But hopefully you are uselocked to yourself so that nobody
can execute commands on you at all."
Lukthil says, "Technically it's not dark though, Thran, since the dark flag
is never set. It's just an unconnected player object."
Krutt grins, "I am now... btw, can attributes be uselocked?"
Paleran says, "@atrlock"
Thranduil nods. "What about new created players. Are they now set up with
uselocks..?
Sabrina says, "Yeah, they are Mazzie."
Lukthil says, "No, individual attributes cannot be uselocked per se. You
should put code in the attribute to lock it."
Krutt says, "Thanks Paleran, I have been trying to figure that one out for
a long time."
Lukthil says, "I.e.
&attr object=@switch %#=<your dbref>,{do stuff},{@pe/s %#=Nope.}"
Thranduil is old, Sabs. Was not uselocked back then.
Paleran nods, "Lukthil's suggestion is a better route, though."
Sabrina grins because this has turned into a "Ask Luk anything you can
possibly think of while you know he's around!" session.
Azakhil thinks new players should default to be @elock, @lock'd and and
@ulocked, are they now?
Sabrina grins at Mazzie.
Nolindil laughs
Lukthil says, "Yes, now players are automatically uselocked to themselves
upon creation."
Azakhil says, "But @elock?"
Lukthil says, "Yes, elocked too."
Sabrina says, "as a =name and not just name even :)"
Lukthil says, "In the old days ulock wasn't done because ulock was a page
lock when on players. There was no player version of ulock."
Lukthil says, "More questions?"
Nolindil says, "Example time! :)"
Paleran wants to see code :)
Azakhil says, "OK...I knew of problems from people getting robbed that
way...with no @elock...of course, some people still turn it off
and forget too....."
Belegril says, "Examples! :)"
Lukthil says, "Okay, if you're up to it, we'll go on to examples."
Kloi_Whitebeard has arrived.
Kloi_Whitebeard bounces in on a bright red pogo-stick, he's obviously OOC!
Sabrina says, "How many M&Ms in a bag of plain M&Ms?"
The sun flashes brightly on the horizon. Night gives way to morning.
Thranduil jumps up and down! Yay!
Belegril says, "Umm, actually, I got rid of the nested tripled iter().
Still, got a fair number of them. :)"
Lukthil says, "One... two... three... *crunch*. Three."
Paleran says, "Abbout 40...."
Krutt hrms, "One quick question."
Belegril raises his hand.
Lukthil says, "Let me say something by word of preface before we start the
examples."
Belegril nods.
Belegril says, "Some people set themselves link_OK, Aza. :)"
Kloi_Whitebeard has left.
Lukthil says, "I want to emphasize the purpose of these examples. These are
EXAMPLES. The point of this is to use actual code to demonstrate
how the techniques we've discussed can be applied and to potentially
bring up new things that we might not have thought to talk about.
The point is NOT to serve as coding slaves for you. :) I will be
picking and choosing what we will work on and what we won't based
on concepts I see that could be illustrated."
Nolindil says, "Log for the questions part of class is 72kbytes before
editing. We'll see if cutting my private discussions out reduces
it a lot. :)"
Belegril says, "Of course. I have a quite conceptual piece of code, I
hope. :)"
Paleran had an example....Doosy, too...But I figured out myself while
raking :)
Lukthil says, "Note that big ugly things that are hard to understand what
they are supposed to do in the first place are not so useful.
Understanding the problem is an important part of learning. :)"
Nolindil grins
Belegril says, "Umm, this might be a drawback of my example. :)"
Lukthil says, "Krutt, you had one final question?"
Nolindil has good code to show off, and code problems to share. Which do
you want?
Paleran has only good code ;)
Azakhil is more interested in seeing examples
Thranduil gees.
Thraile has lots of bad code
Belegril also has a good code. Just hard to read sometimes. :)
Sabrina admits to being human and having code that isn't perfect. :)
Lukthil says, "Also note that these code examples will be logged along
with the rest of this and will be made available to anyone via
ftp so don't offer stuff that's secret or you don't want stolen. :)"
Krutt nods, "Yes, I just wondering the time differences in execution of
nesting @swi statements and chaining them."
Paleran was kidding...95% of my code is horrible until I work over it
three or four times.
Thranduil grins and thinks we all have code that can be worked much better :P
Lukthil says, "Nesting @switches means an extra queue pass for each nested
level. That makes them in general extremely slow."
Lukthil says, "Nesting switch() on the other hand is quite fast in
comparison."
Sabrina grins, "All of my code is horrible until someone else looks it
over. ;)"
Krutt nods, "That is what I figured, I try to chain them when I can."
Lukthil says, "I will be very surprised if anyone has anything that can't
be improved."
Paleran laughs, "Yeah..True Mazzie...I'd love to see Lukthil efficiencize
this one I've been working on lately :)"
Paleran raises his hand, "I do :)"
Krutt hrms, "Okay, thanks Lukthil."
Lukthil says, "Belegril has been pining away. What do you have for us, Bel?"
Belegril says, "My code deals with having to invert shuffle()'d permutations
and other stuff."
Belegril says, "A quick introduction to the problem if no one objects?"
Azakhil tries to avoid nested switches and go for @swi a/b/...=result/*/..,
and so on....
Belegril says, "No one objects? :)"
Lukthil says, "Okay.. shuffle is a good example of a function that gives
the mush a succinct instruction as to how to do a relatively complex
operation."
Belegril says, "Well, this problem arises in my ship code. Every ship has a
number of sails and each of them has some hitpoint. The sails
hitpoints are stored in an attribute of the sails object in the
following format."
Belegril says, "&sails-hp <sails>=5|6|7 10|12|11|3 2|5|4 (as an example)."
Azakhil waves bye, "gotta go...I'll peruse the log...Thanks Luk!
Thranduil wavers.
Lukthil waves
Paleran says, "You can't avoid nested iter()s there."
Sabrina says, "Toodles"
Belegril says, "This means that the ship has three masts. The sails on the
first mast have 5, 6,7 hitpoints respectively, on the second one --
10,12,11,3, etc."
Lukthil says, "Well let's wait and see, Pal."
Azakhil heads back to Middle-earth.
Azakhil has left.
Belegril says, "I avoided them, Pal. :)"
Belegril grins.
Paleran ooos, "Love to see how ya did it, then :)"
Lukthil says, "Certain functions like vadd() and vsub() will be useful
here I'm thinking."
Belegril says, "Now, we know that hp tend to get lower in combat. The
problem is to repair them."
Belegril says, "Yup."
Belegril says, "So, at each dawn/dusk the ZMO would trigger the repair
process."
Lukthil says, "Don't need dawn/dusk trigger."
Paleran wonders if there isn't a function Lukthil doesn't think of
immediately :)
Nolindil says, "He wrote the code, he has them all memorized."
Sabrina thinks there isn't.
Belegril says, "There is a function that calculates the number of hp
(if any) that has to be added to the total. But one has to
distribute them."
Belegril says, "How come, Luk?"
Lukthil says, "Use lazy evaluation. Who cares what the status of the
sails are until somebody looks. Instead of using get() to get
the sail status, use a ufun that figures out if there should
have been repairing going on this whole time and if so, adds it in."
Belegril says, "I don't understand. Do you mean coding a $repair command?"
Nolindil hehs. "In other words, store date of last damage, the calculate
repairs from that time when someone tries to board the ship again.
Lukthil says, "Precisely, Nolindil."
Belegril says, "Umm, possible."
Belegril says, "Anyway, this makes the whole outer iter() redundant, but
still..."
Lukthil says, "The combat resources are all coded this way. Those things
don't do a thing until somebody checks to see how much ore they
have. Then they figure out how much they should have been making
all along."
Belegril says, "Well, so there is function that gets evaluated once it is
triggered. %0 is the number that expired."
Belegril says, "number of hours."
Lukthil says, "Let me sketch the basic format for you..."
Belegril says, "Sorry?"
Lukthil says, "Right now in your code there are places all over the place
that use get() or some similar function to read the stats attr, right?"
Belegril says, "Yup."
Lukthil says, "What you do is you make a special ufun. What that ufun does
is this: There's an attribute somewhere that has the time that the
sails' hp were last looked at on it. (I'll explain in a moment how
to set that part up)."
Belegril says, "So, the basic idea in coding it is as follows."
Belegril says, "I get it, Luk. :)"
Belegril waits before continuing.
Lukthil says, "This ufun reads that attribute and based on the difference
between that time and the current time, it calculates how much
repair should have been done."
Lukthil says, "But maybe others don't, Bel. This is a class. :)"
Paleran would like to hear, this, actually :)
Thraile thanks luk for continuing
Belegril says, "Precisely. That's what it does in its current version.
10 hours for &daytime and 14 for &nighttime. But I'll change it,
of cours.e"
Nolindil says, "More properly, for ship repair, you might prefer to store
the time that the ship returned to its dock?"
Lukthil says, "Then what it does is it sets the hp attribute with the new
values AND it sets the attribute that's supposed to store the time
with the CURRENT TIME. And then it returns the value that it just
set in the stats attr."
Belegril says, "831609669"
Belegril says, "[secs()]"
Lukthil says, "secs() Yes. :)"
Krutt has left.
Paleran says, "831609692.."
Krutt has arrived.
Paleran hrms.
Krutt says, "Sorry about that..."
Thraile waves and goes to bed
Lukthil says, "Now you have this magic ufun that both updates the sails hp's
and returns the value of the hp's at the same time."
Belegril says, "831609715"
Paleran says, "secs()?"
Belegril hmms.
Paleran cools :)
Lukthil says, "What you then do is replace all calls to the get() that reads
the stats with instead a call to this ufun."
Sabrina will brb!
Nolindil censors all these bad words...
Belegril says, "Cool. Thanks, Luk. :)"
Lukthil says, "The only thing that you have to be careful about is that if
there's anything that doesn't look at the sails but would change
anything about how the repairs are being done, you must make that
code look at the sails and then just toss the result. That forces
an update of their stats."
Belegril says, "Actually, I have to count in more... The rates of repairs
depends on whether the ship is at sea or in the port and on whether
it was moving or fihgting recently or not."
Lukthil says, "So everytime the ship moves, you need to set something
somewhere that indicates that."
Thraile has disconnected.
Thraile heads back to Middle-earth.
Belegril says, "Yeah, I do it with @amove on the ships (@atrlocked, of
course). When the ship moves it 'uses' it distantly, which updates
t&resting and &last-location. Two at the same time to thwart those
who move the ship when halted."
Belegril says, "Double check. :)"
Lukthil says, "Note that a remote use is doable by anyone so if there's a
security risk involved there, you will need to @switch on %# in the
amove code."
Belegril says, "It does a switch on the list of the ships' dbrefs."
Lukthil says, "That'd do it then."
Belegril says, "can I move then to the attribute itself? :)"
Lukthil says, "You will likely need to toy with the code a bit to make sure
it takes everything into account but that's the basic idea."
Lukthil says, "Hmm?"
Belegril says, "Sure. :)"
Nolindil will try to break it for you... :)
Lukthil says, "Does anyone have any questions about how we handled this
coding problem?"
Belegril says, "Well, we get the amount of new hp to add, but how to
distribute them randomly? recall that &sails-hp is a space-separated
list of |-separated lists. :)"
Lukthil says, "Bel: Depending on how you wish the distribution to be done,
I would split the sum into a space-separated list and then just
insert the | delimiters into the proper places."
Thranduil grins, had to afk for awhile and compleatly lost the thread..
read about it later..
Belegril says, "Yeah, up to one point. :) The sails don't have to be
repaired form the left to the right always. :)"
Belegril says, "from even"
Belegril says, "That is what I use shuffle() for."
Lukthil says, "Perhaps you would want to create a customization option.
Place the sails on a priority list. Have it repair from top to
bottom of the list."
Belegril says, "Umm, maybe. Interesting idea."
Lukthil says, "Afterall, the captain could choose to repair one thing
before another."
Belegril says, "So far it does the things completely at random."
Lukthil says, "I priority list might actually lessen the computations and
add functionality at the same time."
Lukthil says, "I->A"
Belegril says, "Right. I should think of how to make the customization
command friendly, though. :) Typing 23 sail-names one after one,
with some as long as 'main topgallantstaysail' is rathe rtedious.
Pure numbers is not too IC-appealing."
Lukthil says, "I think I'm going to have to cut the class short. I wanted
to end on the hour. Since there seems to have been some response to
this, I will likely be offering another identical class in the
future which will allow you to bring your examples back. Also as
you know, I am always around to help you with things and I read my
+mail dutifully so feel free to ask for help with things."
Paleran grins and would love to see you create one of his functions better...
Lukthil says, "Bel: A nice command might be +priority sailname=rank. Command
would remove the sail from it's current position in the list and
add it to the rankth position."
Thranduil smiles and nods at Luk, "It's been great. - And another one would
be very appriciated with examples and such.
Belegril says, "Cool. Thanks again. Very helpful class, Luk. :)"
Sabrina grins and will have fixed her booboo by then, but that's okay. :)
Lukthil says, "Let me just end with a parting comment..."
Lukthil says, "One thing to keep in mind as a programmer is to dare to look
at problems in new ways. Breaking "mental fixations" as they call
them is a big part of general problem solving and it applies to
code too. A good piece of code is much like a work of art. You
look at it once and it's not totally obvious what is going on. And
after you stare at it a while you eventually say... "oh wow that's
really clever". That's when you know you've got something that is
refined, efficient, and does the job well. I hope these techniques
come in handy. Don't be afraid to use them. :)"
Sabrina wows, something serious as a parting comment, Luk? I'm surprised!
Lukthil says, "Hey code is beautiful. Can't deny it. :)"
Paleran smiles, "I use it all the time, Luk :) THanks for the calss...It was
most insightful!"
Belegril smiles, "We'll try." :)
Sabrina chuckles
Krutt grins, "You got that straight Lukthil!"
Sabrina gives da teach a cookie, "Thanks for da class!"
Belegril bows to the teacher.
Thranduil smiles and hopes she will one day be able to do such codework
since art aint anything she can do :P
Belegril has left.
Thranduil gives Luke the apple she's been savin', "Thanks alot.:)
Lukthil grins and waves.