Closed
Bug 70743
Opened 24 years ago
Closed 24 years ago
nsIURI::SchemeIs() should take a const char* so it is extensible.
Categories
(Core :: Networking: Cache, defect)
Tracking
()
RESOLVED
FIXED
mozilla0.9
People
(Reporter: jud, Assigned: jud)
References
Details
Attachments
(1 file)
currently IsScheme takes a hardcoded enum to do a scheme query. this methodology
is not extensible because the schemes are hardcoded and other protocols cannot
use this. If the param were an nsIAtom, we maintain extensibility.
its delibrate. The intent is for optimizing on common cases. For all of the rest
you can always getScheme and do the comparison yourself.
| Assignee | ||
Comment 2•24 years ago
|
||
understood, but nsIAtom can be used for the optimization purpose, and it's also
extensible.
| Assignee | ||
Comment 3•24 years ago
|
||
changing comment from: "nsIURI::IsScheme() should take an nsIAtom so it is
extensible.
"
After talking this through at the API review meeting (see
http://www.mozilla.org/projects/embedding/apiReviewNotes.html#nsIURI ), we
concluded that the method should take a "const char*", do a first char check,
then a string compare. Something like...
uriImpl::schemeIs(const char * aScheme) {
if (*aScheme != *mScheme) return PR_FALSE;
if (PL_strcmp(aScheme, mScheme)) return PR_FALSE;
return PR_TRUE;
}
This will allow it to be extensible, as well as performant.
Keywords: mozilla0.9
Summary: nsIURI::IsScheme() should take an nsIAtom so it is extensible. → nsIURI::SchemeIs() should take a const char* so it is extensible.
The whole purpose of schemeIs was to provide a faster compare than the string
compares, purely for performance reasons. The 'hard-coded' list of schemes is
there for a good reason-- these compares are needed frequently and so reducing
the compares to that of an int made sense. I still am not convinced that we need
to change this signature.
| Assignee | ||
Comment 5•24 years ago
|
||
taking bug, I have a patch for this and will post it here after my build completes.
Assignee: gagan → valeski
| Assignee | ||
Updated•24 years ago
|
Target Milestone: Future → mozilla0.9
jud: it doesn't matter who has the bug. Its more important that the change you
are proposing is correct.
| Assignee | ||
Comment 7•24 years ago
|
||
agreed. I believe I've stated my case though, and I haven't heard rebuttal. it
looks like the original optimization was made partially because of some
strduping that was going on. strduping + strcmp would indeed be heavy. I'm
posting a patch that does no strduping, and rarely does strcmps. Regardless, the
api as it stands cannot be extended at run time.
| Assignee | ||
Comment 8•24 years ago
|
||
| Assignee | ||
Comment 9•24 years ago
|
||
patch does the following:
1. updates callers of ->schemeIs() to use regular strings instead of the enum types.
2. updates implementors of ::SchemeIs() to do the char * comparison. these impls
do a first char verification check first to avoid strcmp as much as possible.
3. nsHTTPHandler - added a char* mScheme protected memeber variable so
inheriting classes (nsHTTPSHandler) can replace the scheme.
4. nsHTTPSHandler - removed static create method in favor of the generic init
constructor (in nsNetModule.cpp). Added an Init() method which calls
nsHTTPHandler::Init() and replaces the mScheme w/ "https"
Comment 10•24 years ago
|
||
hey gagan,
Basically, what the change involves is removing the hardcoded enumeration and
performing a byte comparision (using an extensible string) rather than an
unsigned long comparision (using a hardcoded enumeration).
I *really* doubt if anyone will see the performance difference between the byte
and long comparisions :-) However, the result is *huge* because it opens up a
*key* API for future extensibility!
There is absolutely no reason why some protocols should be more "special" than
others in our APIs...
-- rick
Comment 11•24 years ago
|
||
sr=rpotts
Comment 12•24 years ago
|
||
My only reasoning for keeping some cases as special was becuz of the frequency
with which they are used. Other than that, the patch looks ok to me. I just had
one concern-- why don't we ensure mScheme to be lower case in nsSimpleURI just
like nsStdURL instead of the mLower check? Seems like it would be easiar to do
the same stuff in nsSimpleURI.
| Assignee | ||
Comment 13•24 years ago
|
||
yea, I had the same question/concern buzzing around in my mind. the only reason
I left it was that I was thinking that it's *possible* that some simple URI
usage may need to conserve the the case of the scheme? I mean, we know our
stdURL impl and its usage wants lower-case, but what about UR_I_s? Being the
more generic, conceptually, of the two, is it legal for lower-case schemes?
I'd love to get rid of the mLower var and do a toLower on the scheme of
nsSimpleURI, but I'm not sure if that would hurt some simple URI users; thoughts?
Comment 14•24 years ago
|
||
all uri schemes are expected to be case-insensitive (simple or otherwise) and so
doing the lower case makes sense. with that change r=gagan
Component: Networking → Networking: Cache
| Assignee | ||
Comment 15•24 years ago
|
||
fix is in. thanks all.
Status: NEW → RESOLVED
Closed: 24 years ago
Resolution: --- → FIXED
Comment 16•24 years ago
|
||
I was in this bug looking to see if the ``id (*i_Scheme...'' thing was in the
reviewed patch, and noticed your wacky byte-compare-then-string-compare thing?
What's the deal? If you want a fast string compare:
- use the system strcasecmp, rather than going through the silly extra
layers of PL_strcasecmp
- don't check the first byte again in strcmp, once you've checked it inline!
(I think PL_str* should be a shooting offense, myself, but hey.)
I'm also not thrilled about seeing code that could never have been compiled by
the author, and was obviously not compiled or read carefully by the reviewers,
being slammed into the tree the day before 0.8.1. Again, what gives?
| Assignee | ||
Comment 17•24 years ago
|
||
< use the system strcasecmp, rather than going through the silly extra
< layers of PL_strcasecmp
If you've got a better way of doing strcmps, sweep the tree and change the
existing usages, then do something to make sure people don't use the suboptimal
stuff.
< don't check the first byte again in strcmp, once you've checked it inline!
Yea, the reason I didn't skip the first byte was:
1. I figured it would confuse someone looking at the code.
2. falling into the PL_ compare was is generally a rarity so not much of a perf hit.
obviously you looked at the code though and noticed the better case, so I'll
submit a patch for that.
< (I think PL_str* should be a shooting offense, myself, but hey.)
good for you, make a difference and change the way we use (or don't) PL_*
| Assignee | ||
Comment 18•24 years ago
|
||
I'd like to note that the bustage was in a directory that is *not* turned on by
default in regular builds. *all* three platforms were built (default pull/build)
before checking in. things that aren't in default builds obviously can't benefit
from regular pull/build error checking.
I apologize for the typo.
| Assignee | ||
Comment 19•24 years ago
|
||
after talking about this a bit w/ Rick, we'll leave the changes as they stand.
skipping the first byte would add readability complexity, and could potentially
be more expensive than the extra byte compare as the pointers need to be
incremented by one in order to do the skipping. Also, falling into the PL_strcmp
is an edge case to begin w/.
Comment 20•24 years ago
|
||
All https vs http comparisons will fall into the strcmp. In fact, won't most
URL's be http? That means to know they're really the same--the usual case--we
take the performance hit.
| Assignee | ||
Comment 21•24 years ago
|
||
I believe most urls are chrome/resource, http comes next.
the heaviest usage of a http/https check is in image lib and that hunk of code,
I've verified w/ pavlov, won't exist in the new imagelib.
the next heaviest usage is probably shouldaddtoglobalhistory.
maybe SchemeIs() should take the len of the string too? that would widdle it
down even further and nix the http/https case.
| Assignee | ||
Comment 22•24 years ago
|
||
or, we could use atoms instead.
Comment 23•24 years ago
|
||
If this discussion has come down to atoms then I have to say that we have
covered a full circle. I did consider atoms as a potential parameter to check on
when I first did SchemeIs, but the problem was that atoms do not have a clean
ownership model across different components. Creating a new atom each time was
not necessarily cheap. I landed up with "faking" atoms with the hardcoded enum
list. Since the SchemeIs function was purely an optimization we sacrificed
extensibility for performance. I am still not completely convinced that we need
to keep this extensible for other schemes (that aren't being used in the product
as yet, but would be common enough to be optimized for) but I'd let jud make
that call.
| Assignee | ||
Comment 24•24 years ago
|
||
at first glance it looks to me like the ->SchemeIs() callers can cache their
atom's so I don't see the ownership issue.
there are a few embeddors I can think of that are already impl'ing their own
proto handlers.
Comment 25•24 years ago
|
||
Before we start worrying about the "performance hit" of doing a strcmp vs an
unsigned long comparision, let's remember why this API was created in the first
place.
Before IsScheme(...) was introduced, the caller was forced to call
nsIURI::GetScheme(...) which involved a buffer copy and then do a strcmp() and
finally free the buffer which held the scheme.
Clearly, in that situation, the calls to allocate and deallocate memory far
outweighed the strcmp() call in terms of time.
In Jud's new code we, at most, make a call to strcmp()... I'm sure we could
even unroll the strcmp and write a quick comparision loop inline if necessary.
But, *please* lets collect some data before we call this change a perfomance
hit and start trying to "fix" it!!
-- rick
| Assignee | ||
Comment 26•24 years ago
|
||
I just did a quick sanity check on the number of times SchemeIs() gets called.
I set conditional break points in simple::SchemeIs() and std::SchemeIs(), I
asked them to break when they fell into their strcmp case 1000 times (and in
stdurl::schemeis() after it fell into it's full strcmp 500 times). after
launching mozilla (w/ mail/news opening at startup w/ 5k messages in inbox), and
visiting www.abcnews.com (a "heavy" page), no breakpoints hit. After hovering on
one of the abcnews links, I got the stdURL::SchemeIs()::strcmp version to fire.
At that point, the strcmp had been called 500 times, and SchemeIs() was hit 926
times. So, there's an ~50% chance of hitting the full strcmp (not exactly the
edge case I had earlier deduced) in this limited test case. Most of those calls
came from imagelib and I've verified that pavlov has removed that code from
imglib2 and we talked about the strong likely hood of it not coming back.
So, before taking another cut at this, can someone (brendan? rick?) speak to
whether or not a 4 byte strcmp being called 500 times per top level URL load
(which includes inline content), is expensive? I would doubt it.
Comment 27•24 years ago
|
||
I'd just like to know why extensibility of "common cases" is important? Do you
really see a new scheme becoming more common than http/chrome/res/etc...? So
much so that we need to optimize for it?
| Assignee | ||
Comment 28•24 years ago
|
||
why lock ourselves into a nsIURI interface definition for embedding 1.0, then
turn around and produce nsIURI2, for a case we already know is on our plate to
cover.
If this is a perf hit, I propose nsIAtom instead.
| Assignee | ||
Comment 29•24 years ago
|
||
using a debug build and a test driver for SchemeIs(), I tested the following.
Running SchemeIs() 1,000,000 times, w/ "http://www.foo.com" as the uri and
"https" as the check value (our 50% case in SeaMonkey).
PRUint32 compare: average run time was 125,000 micro seconds
nsIAtom compare: average run time was 125,000 micro seconds (expected to be the
same as PRUint32 as it's simply address equality checking)
The current checked in 1st byte, strcmp compare: average run time was 330,000
micro seconds.
homegrown inline strcmp: average run time was 230,000 micro seconds
The test used "http" and "https" which forced us into the full strcmp everytime.
So, current impl is 3x slower than the old one, and the homegrown strcmp version
was 2x slower.
Conclusion: for API consistencies sake (rather than making nsIAtom public) we're
going to leave things as they are. Based on this strcmp only being called 50% of
the time in our "normal" usage which can probably be measured in the tens or
hundreds of thousands rather than millions. I can make the change to knock a 3rd
of the time off, but I just don't see the need at this stage.
Comment 30•24 years ago
|
||
I was arguing the other week with gagan and dougt, before gagan's enum-based
schemeIs change went in, that they were optimizing prematurely. They were not
persuaded, and I didn't see an extensibility barrier (I still don't), so I gave
in and gagan did an ownerly job of getting that change in.
Now, premature optimization is the root of all evil, but in this case, untested
code checkin kinda balances things out! :-/
Since we're talking premature optimization, the first-char test is one, too.
And then reloading and case-folding that char on a tentative match takes back
that premature optimization for that tentative-match case, and bloats the code
to boot. Why not use good ol' strcasecmp and let gcc/glibc (on Linux) handle
the (shared) optimization?
/be
Comment 31•24 years ago
|
||
ok... I give up. I guess I'm the only one in the world that feels that all
protocols should be treated equal by our APIs...
It escapes me why everyone else thinks it's ok to have a method on nsIURI that
only works for *some* protocols!! HELLO - is anybody thinking??
Lets just screw aim:// and any other "unimportant" protocols and force consumers
to do a MEMORY ALLOCATION/DEALOCCATION just to determine the fucking scheme!!
HELLO - is anybody thinking??
After all, if we really are calling this method THOUSANDS OF TIMES per URL load,
then by all means lets make the function fast rather than fixing the real
problem - that we are calling this method THOUSANDS OF TIMES per URL load :-(
HELLO - is anybody thinking??
I guess not - so I'll just shut up
-- rick
Comment 32•24 years ago
|
||
rpotts: hey, I'm on your side -- i don't see a need for the enum-based thing.
Where is the data showing thousands of scheme-tests per URL load?
/be
| Assignee | ||
Comment 33•24 years ago
|
||
using my off the cuff counter based breakpoints, on a heavy load
(www.abcnews.com), we were calling it at worst, say ~400 times (per top level
load. so that includes all the inline content). I didn't get into *who* was
calling any deeper than noticing that il_PermitLoad() was checking to see if it
was http or https (two sequential calls), on each image load. We're leaving it
as is because this won't show up on profiling radar at an app-level. If it does,
we can dig into it then, and at that point we should be asking why it's called
so much, rather than how to make it even faster.
A common calling case was "is the scheme of this url http or https, we only want
to deal w/ http based schemes in this section of code."
Comment 34•24 years ago
|
||
I have a solution that keeps things optimized for standard/known/commonly used
cases and is also extensible! How about we change the signature of SchemeIs to
be something like--
boolean schemeIs(in PRUint32 schemetype, in string scheme);
The implementation stays like the way it was before Jud's checkin and will
ignore scheme if the schemetype is anything but nsIURI::UNKNOWN. And in case the
schemetype is indeed nsIURI::UNKNOWN it can then default to the string compares
(with or without the first letter checks) as Jud wants.
This way we stay at our best performance on compares of common cases and still
make this function work for new ones.
Comments?
| Assignee | ||
Comment 35•24 years ago
|
||
I think doubling up the the signature just adds complexity, and we've determined
we don't want to be optimizing at this in-perceivable level.
You need to log in
before you can comment on or make changes to this bug.
Description
•