Closed
Bug 112708
Opened 23 years ago
Closed 23 years ago
preferences is malloc-happy
Categories
(Core :: Preferences: Backend, defect, P2)
Core
Preferences: Backend
Tracking
()
RESOLVED
FIXED
mozilla0.9.8
People
(Reporter: alecf, Assigned: alecf)
References
Details
(Keywords: memory-footprint, Whiteboard: fix in hand)
Attachments
(8 files, 6 obsolete files)
(deleted),
patch
|
bnesse
:
review+
dveditz
:
superreview+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
bnesse
:
review+
dveditz
:
superreview+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
bnesse
:
review+
brendan
:
superreview+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
dveditz
:
superreview+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
bnesse
:
review+
brendan
:
superreview+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
bnesse
:
review+
sfraser_bugs
:
superreview+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
alecf
:
review+
sfraser_bugs
:
superreview+
|
Details | Diff | Splinter Review |
(deleted),
patch
|
brendan
:
superreview+
|
Details | Diff | Splinter Review |
libpref is mallochappy and allocates lots of little bytes.. for example, it
allocates a PrefNode for each pref:
typedef struct
{
PrefValue defaultPref;
PrefValue userPref;
PRUint8 flags;
} PrefNode;
(PrefValue is a union 4 bytes long)
Thats 9 bytes, probably rounded up to either 12 or 16 bytes depending on the
allocator.
it also makes a copy of the pref name, which varies in size, but is probably
20-50 bytes long.
If the pref is a string, it stores the string value, which also varies in
length, but is probably 10-80 bytes long.
Finally, it makes a PLHashEntry, which is probably 16 bytes.
My plan is to either use one big arena, or use one arena for the strings, and
nsFixedSizeAllocator for a combined PrefNode / PLHashEntry.
Wish me luck.
Assignee | ||
Comment 1•23 years ago
|
||
First step is making prefapi compilable as C++. I needed to add a bunch of
casts.
Assignee | ||
Comment 2•23 years ago
|
||
I needed to make some minor changes to make sure extern's matched up, etc.
Platform issues:
- Win32: I did not need to change makefile.win because the windows build system
automatically picks up which file to compile (i.e. prefapi.cpp, when it
appears)
- Unix: had to modify Makefile.in
- Mac: had to modify projects
Looking for reviews.
Assignee | ||
Comment 3•23 years ago
|
||
I'd like to get these patches in 0.9.7 so I can start the 'real' work for 0.9.7.
I'm looking for reviewers, can I get r=dp or r=bnesse and then sr=dveditz? see
my earlier comments about the platform changes (specifically, why I changed
Makefile.in but not makefile.win)
Status: NEW → ASSIGNED
Priority: -- → P2
Target Milestone: --- → mozilla0.9.8
Updated•23 years ago
|
Attachment #59758 -
Flags: superreview+
Updated•23 years ago
|
Attachment #59759 -
Flags: superreview+
Comment 4•23 years ago
|
||
sr=dveditz
Comment 5•23 years ago
|
||
Comment on attachment 59758 [details] [diff] [review]
add casts so that prefapi.c can be renamed to prefapi.cpp
Boy there's a lot of crap in this file that should go away... r=bnesse.
Attachment #59758 -
Flags: review+
Updated•23 years ago
|
Attachment #59759 -
Flags: review+
Comment 6•23 years ago
|
||
Comment on attachment 59759 [details] [diff] [review]
rest of patch required to switch the module over
r=bnesse.
Assignee | ||
Comment 7•23 years ago
|
||
thanks guys - the first patch and the prefapi_private_data.h file have been
checked in. The rest will wait for a carpool or some sort of cross-platform
coordination tomorrow.
Comment 9•23 years ago
|
||
This checkin was just converting the file from C to C++ as a prelude to any
actual improvement.
Assignee | ||
Comment 10•23 years ago
|
||
right... and after that, I'm switching over to PLDHash (I'm sure brendan &
shaver will be happy) with the default allocators. That ought to reduce the
NUMBER of allocations by about 25% and the TOTAL SIZE of THOSE allocations by
about 22%...
Basically I'll be combining the hashentry (16 bytes) and the PrefNode (9 bytes)
into a unified structure that is only 17 bytes. I've got that much in my tree
right now (but requires some of this C++ juggling first)
Then I'll switch over to nsFixedSizedAllocator for these 17-byte objects.
After that, we will have lots of variable length strings, and I'm not sure what
the best course of action there is. All the strings aren't handed to us right
away. I'm thinking that I'll use an arena or two for them.
Comment 11•23 years ago
|
||
Awesome. Please reduce the inital size of the hash table from 2048 to something
like 256. That should suffice.
Assignee | ||
Comment 12•23 years ago
|
||
there are 733 default prefs in the default mozilla build. I'm going to set it to
1024 for now. I realize that yes, it will grow as necessary, but each growth
requires an allocate-and-copy on each growth, so I don't see the benefit of
making it significantly smaller than the number of prefs. besides, if I set it
to 256, it will likely double twice to 1024 anyway, so we might as well start
there.
Also, mail creates a number of dynamic prefs which don't exist in the default
prefs (i.e. a whole set of about 20+ for each mail account) and the commercial
build may have more as well.
These pref objects are going to be about 20 bytes, so making it 1024 vs. 733
only wastes about 5k of one-time space.
Assignee | ||
Comment 13•23 years ago
|
||
ok, I'm not sure how I pulled this off, but I feel intimately familiar with
pldhash now :)
Anyway, this patch converts us over to PLDHash and we'll be much less
malloc-happy.
After I land _this_, then I'll start using nsFixedSizedAllocator to allocate
the PrefEntry structs.
Assignee | ||
Comment 14•23 years ago
|
||
adding brendan and shaver for PLDHash-related reviews. c'mon, you know you
wanted someone to give libpref a spanking.
Comment 15•23 years ago
|
||
Comment on attachment 59905 [details] [diff] [review]
switch prefs over to pldhash
Remove, don't comment out:
-#include "plhash.h"
+/*#include "plhash.h"*/
+#include "pldhash.h"
You might rather use PLDHashEntryHdr as the public base class and declare your
own char *key; member.
Then use NS_STATIC_CAST to downcast from PLDHashEntryHdr* to your Entry*.
More commenting out whinage from me -- I trust you to clean up later, but why
wait?
-PLHashTable* gHashTable = NULL;
+PRBool gHashTableInitialized = PR_FALSE;
+//PLHashTable* gHashTable = NULL;
+PLDHashTable gHashTable = { 0 };
PRBool gEnumeratingHashTable = PR_FALSE;
Hmm, I thought & in front of a function name was redundant in C and meant
something else in C++? Maybe it's just redundant in both.
+ &matchPrefEntry,
+ PL_DHashMoveEntryStub,
+ &clearPrefEntry,
It's fair to use test whether .ops is null to decide when to initialize a
static PLDHashTable, so you can get rid of gHashTableInitialized. Just take
care to clear .ops on Init failure and after any Finish call.
/be
Assignee | ||
Comment 16•23 years ago
|
||
oops, sorry 'bout those comments - I had meant to take at least those out :)
Anyway, I addressed everything you mentioned - I like the NS_STATIC_CAST thing
- switching over actually found one place where I was casting away const-ness.
Attachment #59905 -
Attachment is obsolete: true
Assignee | ||
Comment 17•23 years ago
|
||
This patch makes it so that we use nsCAutoString and friends instead of
PR_smprintf and other malloc-happy functions, such as str_escape.
Comment 18•23 years ago
|
||
Comment on attachment 59914 [details] [diff] [review]
switch prefs over to pldhash, v2
>-PR_STATIC_CALLBACK(PRIntn) pref_enumChild(PLHashEntry *he, int i, void *arg)
>+PR_STATIC_CALLBACK(PLDHashOperator)
>+ pref_enumChild(PLDHashTable *table, PLDHashEntryHdr *heh,
>+ PRUint32 i, void *arg)
> {
>+ PLDHashEntryStub *he = (PLDHashEntryStub*)heh;
Why not NS_STATIC_CAST(PrefHashEntry*, heh) here, and he->key without the
(char*)
cast below?
> EnumerateData *d = (EnumerateData *) arg;
NS_REINTERPRET_CAST for style points.
>+typedef struct : PLDHashEntryHdr
> {
>+ char *key;
>+ PrefValue defaultPref;
>+ PrefValue userPref;
>+ PRUint8 flags;
>
>+} PrefHashEntry;
Now that these files are written in C++, no need to typedef an anonymous
struct,
just move the PrefHashEntry typename to after the struct keyword. (Get rid of
the
empty line after the last member, while you're at it.)
>+void PR_CALLBACK
>+clearPrefEntry(PLDHashTable *table, PLDHashEntryHdr *entry)
> {
>+ PrefHashEntry *pref = NS_STATIC_CAST(PrefHashEntry *, entry);
> if (pref)
> {
> if (pref->flags & PREF_STRING)
No need to null-check pref (or entry) here.
>+PRBool PR_CALLBACK
>+matchPrefEntry(PLDHashTable*, const PLDHashEntryHdr* entry,
>+ const void* key)
>+{
>+ const PrefHashEntry *prefEntry =
>+ NS_STATIC_CAST(const PrefHashEntry*,entry);
>+
>+ if (prefEntry->key == key) return PR_TRUE;
>+
>+ if (!prefEntry->key || !key) return PR_FALSE;
>+
>+ return (strcmp((const char*)prefEntry->key, (const char*)key) == 0);
Ick, no need for (const char*) cast in front of prefEntry->key. Mebbe declare
a
const char* otherKey = NS_REINTERPRET_CAST(const char*, key) local to match?
>+PLDHashTable gHashTable = { 0 };
s/0/nsnull/
> if (!gMochaTaskState)
> {
>+
Extra spaces at end of empty line, and this added empty line seems useless to
me
(it's unconventional after an opening brace, esp. a brace on its own line, to
add
yet another vertical separator). Nit-pick worth fixing?
>@@ -738,18 +740,18 @@
> if (tmp_str)
> {
> prefEntry = PR_smprintf("user_pref(\"%s\", \"%s\");",
>- (char*)he->key, tmp_str);
>+ (char*)pref->key, tmp_str);
No need for (char*) before pref->key -- check elsewhere.
>+PR_STATIC_CALLBACK(PLDHashOperator)
>+pref_ClearUserPref(PLDHashTable *table, PLDHashEntryHdr *he, PRUint32,
>+ void *arg)
> {
>- PrefNode *pref = (PrefNode *) he->value;
>+ PrefHashEntry *pref = NS_STATIC_CAST(PrefHashEntry*, he);
> PR_ASSERT(pref);
>
> if (pref &&
No need for null checks here.
>+static inline PrefHashEntry* pref_HashTableLookup(const void *key)
> {
>- return gEnumeratingHashTable ?
>- PL_HashTableLookupConst(gHashTable, key) :
>- PL_HashTableLookup(gHashTable, key);
Get rid of gEnumeratingHashTable?
>+ PrefHashEntry* result =
>+ NS_STATIC_CAST(PrefHashEntry*,PL_DHashTableOperate(&gHashTable, key, PL_DHASH_LOOKUP));
Space after that first comma? Mebbe put the PL_DHashTableOperate call on its
own
line for clarity.
>@@ -1483,13 +1481,13 @@
> (JSContext *cx, JSObject *obj, unsigned int argc, jsval *argv, jsval *rval)
> {
> /*void* value = NULL;*/
>- PrefNode* pref;
>+ PrefHashEntry* pref;
> /*PRBool prefExists = PR_TRUE;*/
>
> if (argc >= 1 && JSVAL_IS_STRING(argv[0]))
> {
> const char *key = JS_GetStringBytes(JSVAL_TO_STRING(argv[0]));
>- pref = (PrefNode*) pref_HashTableLookup(key);
>+ pref = NS_STATIC_CAST(PrefHashEntry*, pref_HashTableLookup(key));
No need to cast here, eh?
/be
Assignee | ||
Comment 19•23 years ago
|
||
all comments addressed again - and was proactive about avoiding unnecessary
casts
I had forgotten, when I switched to making PrefHashEntry inherit from
PLDHashEntryHdr, to look in nsPrefBranch.cpp for uses of PLDHashEntryStub..
that's why there were all those unnecessary casts
in order to share the structure between the files, I moved it into prefapi.h.
hows this one look?
Attachment #59914 -
Attachment is obsolete: true
Comment 20•23 years ago
|
||
Comment on attachment 60189 [details] [diff] [review]
switch prefs over to pldhash, v3
>@@ -817,13 +820,16 @@
> return NS_OK;
> }
>
>-PR_STATIC_CALLBACK(PRIntn) pref_enumChild(PLHashEntry *he, int i, void *arg)
>+PR_STATIC_CALLBACK(PLDHashOperator)
>+ pref_enumChild(PLDHashTable *table, PLDHashEntryHdr *heh,
>+ PRUint32 i, void *arg)
> {
>- EnumerateData *d = (EnumerateData *) arg;
>+ PrefHashEntry *he = NS_STATIC_CAST(PrefHashEntry*,heh);
>+ EnumerateData *d = NS_REINTERPRET_CAST(EnumerateData *, arg);
> if (PL_strncmp((char *)he->key, d->parent, PL_strlen(d->parent)) == 0) {
No need for (char *) above?
> d->pref_list->AppendElement((void *)he->key);
NS_REINTERPRET_CAST for consistency?
>@@ -1070,19 +1035,20 @@
> #endif /* XP_MAC */
>
> /* Delete a branch. Used for deleting mime types */
>-int PR_CALLBACK
>-pref_DeleteItem(PLHashEntry *he, int i, void *arg)
>+PLDHashOperator PR_CALLBACK
>+pref_DeleteItem(PLDHashTable *table, PLDHashEntryHdr *heh, PRUint32 i, void *arg)
> {
>+ PrefHashEntry* he = NS_STATIC_CAST(PrefHashEntry*,heh);
> const char *to_delete = (const char *) arg;
> int len = PL_strlen(to_delete);
>
> /* note if we're deleting "ldap" then we want to delete "ldap.xxx"
> and "ldap" (if such a leaf node exists) but not "ldap_1.xxx" */
>- if (to_delete && (PL_strncmp((char*)he->key, to_delete, (PRUint32) len) == 0 ||
>- (len-1 == (int)PL_strlen((char*)he->key) && PL_strncmp((char*)he->key, to_delete, (PRUint32)(len-1)) == 0)))
>- return HT_ENUMERATE_REMOVE;
>+ if (to_delete && (PL_strncmp(he->key, to_delete, (PRUint32) len) == 0 ||
>+ (len-1 == (int)PL_strlen(he->key) && PL_strncmp(he->key, to_delete, (PRUint32)(len-1)) == 0)))
>+ return PL_DHASH_REMOVE;
> else
No else after return non-sequiturs.
>- return HT_ENUMERATE_NEXT;
>+ return PL_DHASH_NEXT;
> }
>
> PrefResult
Ok, finally a non-nit! Sorry for all the fluff, here's some meat:
> PrefResult pref_HashPref(const char *key, PrefValue value, PrefType type, PrefAction action)
> {
>- PrefNode* pref;
>+ PrefHashEntry* pref;
> PrefResult result = PREF_OK;
>
>- if (!gHashTable && !pref_useDefaultPrefFile())
>+ if (!gHashTable.ops)
> return PREF_NOT_INITIALIZED;
>
>- pref = (PrefNode*) pref_HashTableLookup(key);
>+ pref = pref_HashTableLookup(key);
> if (!pref)
> {
>- pref = (PrefNode*) calloc(sizeof(PrefNode), 1);
>+ pref = NS_STATIC_CAST(PrefHashEntry*, PL_DHashTableOperate(&gHashTable, key, PL_DHASH_ADD));
You shouldn't have to hash twice (once for the Lookup, and again for the
PL_DHASH_ADD). Just call PL_DHashTableOperate with PL_DHASH_ADD, and if it
succeeds (doesn't fail with OOM), test pref->key or another initialized by
default to zero member to tell whether you are making a new entry, of hitting
an existing one.
Fix the above and sr=brendan@mozilla.org
/be
Attachment #60189 -
Flags: superreview+
Assignee | ||
Comment 21•23 years ago
|
||
bnesse, do you mind reviewing?
Comment 22•23 years ago
|
||
Comment on attachment 60184 [details] [diff] [review]
use ns*AutoStrings to write prefs to disk
You were going somewhere with this comment I trust? :)
+ // figure out if we're getting our
How about killing PREF_AboutConfig() and pref_printDebugInfo() while your at
it? Less to clean up later.
And finally, could you kill the appropriate function declarations in prefapi.h
too?
With that. r=bnesse.
Attachment #60184 -
Flags: review+
Assignee | ||
Comment 23•23 years ago
|
||
way ahead of you - I actually did kill thos functions in attachment 60189 [details] [diff] [review] :)
Comment 24•23 years ago
|
||
Comment on attachment 60189 [details] [diff] [review]
switch prefs over to pldhash, v3
@@ -817,13 +820,16 @@
return NS_OK;
}
-PR_STATIC_CALLBACK(PRIntn) pref_enumChild(PLHashEntry *he, int i, void *arg)
+PR_STATIC_CALLBACK(PLDHashOperator)
+ pref_enumChild(PLDHashTable *table, PLDHashEntryHdr *heh,
+ PRUint32 i, void *arg)
{
- EnumerateData *d = (EnumerateData *) arg;
+ PrefHashEntry *he = NS_STATIC_CAST(PrefHashEntry*,heh);
Space after the comma please.
@@ -515,7 +497,7 @@
/* Free up gSavedLine to avoid MLK. */
if (gSavedLine)
free(gSavedLine);
- gSavedLine = (char*)malloc(i+1);
+ gSavedLine = (char *)malloc(i+1);
How about some whitespace around that "+"?
@@ -1297,6 +1263,8 @@
switch (type & PREF_VALUETYPE_MASK)
{
case PREF_STRING:
+ // printf("\tSetting old string %s to new string %s\n",
+ // oldValue->stringVal, newValue.stringVal);
Did Brendan already pick on you for this?
@@ -1366,6 +1337,8 @@
pref_SetValue(&pref->defaultPref, value, type);
if (!PREF_HAS_USER_VALUE(pref))
result = PREF_VALUECHANGED;
+ // if ((type & PREF_VALUETYPE_MASK) ==
PREF_STRING)
+ // printf("\tNow value is set to
%s\n", pref->defaultPref);
}
And this?
r=bnesse.
Attachment #60189 -
Flags: review+
Assignee | ||
Comment 25•23 years ago
|
||
awesome, thanks guys. I've addressed everyone's comments here and landed the
pldhash stuff.
adding dveditz for sr= of nsautostring stuff.
Assignee | ||
Comment 26•23 years ago
|
||
here's an updated patch to reflect the patch that just landed.
Attachment #60184 -
Attachment is obsolete: true
Comment 27•23 years ago
|
||
Comment on attachment 60270 [details] [diff] [review]
use nsCAutoStrings, v2
sr=dveditz
Attachment #60270 -
Flags: superreview+
Assignee | ||
Comment 28•23 years ago
|
||
*sigh* - I caused a smoketest blocker, but this is a fix on top of attachment
60270 [details] [diff] [review]
thanks to bnesse for noticing what was going on!
Comment 29•23 years ago
|
||
Comment on attachment 60362 [details] [diff] [review]
only quote strings, not every type
The second prefValue = '\"' (the one right after the str_escape call) should of
course be += (I bugged alecf over irc and he said he fixed this in his tree).
With that, sr=brendan@mozilla.org.
/be
Attachment #60362 -
Flags: superreview+
Comment 30•23 years ago
|
||
Comment on attachment 60362 [details] [diff] [review]
only quote strings, not every type
sigh. r=bnesse.
Attachment #60362 -
Flags: review+
Assignee | ||
Comment 31•23 years ago
|
||
this patch is a MASSIVE cleanup to prefs - I went through with a moderately
fine toothed comb to try to rip out all unused functions, and declare static
the ones that only are used by prefapi.cpp
This should make future work much easier.
can I get an r=bnesse, sr=..?
(Someone who wants to see prefs cleaned up further...:))
Comment 32•23 years ago
|
||
You did a fine job of breaking macpref.cpp (which uses the BinaryPref Stuff) and
I get this neat error in prefapi.cpp...
Error : inconsistent linkage: 'extern' object redeclared as 'static'
prefapi.cpp line 204 static PRBool pref_VerifyLockFile(char* buf, long buflen);
Assignee | ||
Comment 33•23 years ago
|
||
ack! macpref.cpp! we should kill that. Why would it need the binary pref stuff?
do you mind fixing macpref.cpp and attaching a patch to this bug? (thanks for
the platform testing though!)
Assignee | ||
Comment 34•23 years ago
|
||
by the way, you can clean up that linkage error by just removing the
pref_VerifyLockFile declaration from prefapi_private_data.h
Comment 35•23 years ago
|
||
It uses it in some path stuff. I'm not sure if those functions are used or
not... I'll have to search a bit. I think the reason I haven't already killed
macprefs is because they are...
Assignee | ||
Comment 36•23 years ago
|
||
well, as long as the library compiles without linking errors, then we know it's
ok - the only exported function is the XPCOM NSGetFactory, which means nobody is
(or better be) using PREF_GetPathPref() and all the other ones in macprefs.cp
Comment 37•23 years ago
|
||
Comment on attachment 60873 [details] [diff] [review]
massive prefs cleanup
It appears that, with your changes to prefapi.cpp, both macpref.cp and
MacPrefUtils.cpp can be removed from the build.
Please make sure you have emoved the the
pref_VerifyLockFile declaration from prefapi_private_data.h. r=bnesse.
Attachment #60873 -
Flags: review+
Comment 38•23 years ago
|
||
Assignee | ||
Comment 39•23 years ago
|
||
Comment on attachment 61307 [details] [diff] [review]
Mac build changes
cool. r=alecf
Attachment #61307 -
Flags: review+
Comment 40•23 years ago
|
||
Comment on attachment 60873 [details] [diff] [review]
massive prefs cleanup
sr=sfraser
Attachment #60873 -
Flags: superreview+
Comment 41•23 years ago
|
||
Comment on attachment 61307 [details] [diff] [review]
Mac build changes
sr=sfraser
Attachment #61307 -
Flags: superreview+
Assignee | ||
Comment 42•23 years ago
|
||
thanks guys.. cleanup has been checked in
Assignee | ||
Comment 43•23 years ago
|
||
Ok, last patch and then I mark this fixed.
The last place where prefs was malloc-happy was where we were PL_strdup'ing
every pref name, even though all prefs names had the same lifetime, and were
all allocated at the same time.
So I made a little PrefNameBuffer class which allocates space for the strings
in 8k chunks. All the pref names are allocated in this buffer.
I chose 8k because the total bytes allocated to pref names in a basic run with
about 750 prefs is 22k... with this patch, we allocate 3 8k buffers.
bnesse and brendan, do you mind reviewing? Brendan, I'm especially interested
to hear if you think I'm taking the right approach, and if so if I should try
to align the allocated strings at all for speed in strcmp, etc (and if so, if
there's an easy way to tell the "optimal" alignment)
Also, I thought more about the 256 entry vs. 1024 entry issue. The one thing I
did not take into account is that embedded apps should (eventually) have far
fewer prefs - i.e. more in the 200-300 range... so maybe we should start with
256 entries? It looks like the tradeoff is more speed of mozilla-the-browser
vs. memory-used-by-embedded-mozilla.
Comment 44•23 years ago
|
||
prefNameBuffer: I wish we convert pref names into an atom and store them only.
Enumerate would be an issue ugh.
I bet you will see no perf issue with 4k buffers. They are the page size. When
instrumenting malloc cost, we find 4k buffer allocs to be ultra fast.
Assignee | ||
Comment 45•23 years ago
|
||
dp: they are stored as atoms, essentially.. its just that consumers don't use
them... but that's ok because the only overhead cost is hashing - the only time
we do any enumerate-and-compare loops is when calling pref-changed callbacks..
Comment 46•23 years ago
|
||
+// making PrefNameBuffer exactly 8k for nice allocation
+#define PREFNAME_BUFFER_LEN (8196 - (sizeof(PrefNameBuffer*) + sizeof(char*)))
Shouldn't that be 8192?
+PrefNameBuffer::Alloc(PRInt32 len)
+{
+ NS_ASSERTION(this == gRoot, "Can only allocate on the root!\n");
+ NS_ASSERTION(len < PREFNAME_BUFFER_LEN, "Can only allocate short strings\
n");
+ // check for space in the current buffer
+ if ((mNextFree + len) > PREFNAME_BUFFER_LEN-1) {
How about some white space for the "-" sign?
+PrefNameBuffer::FreeBuffer()
+{
+ printf("\t***Freeing a PrefNameBuffer\n");
Hmm, that should be fun in a release build. ;)
+PrefNameBuffer::CopyString(const char *str)
+{
+ if (!gRoot) gRoot = new PrefNameBuffer(nsnull);
+
+ // ack, no memory
+ if (!gRoot) return nsnull;
I'd like to see something more like:
+ if (!gRoot) {
+ gRoot = new PrefNameBuffer(nsnull);
+ // ack, no memory
+ if (!gRoot)
+ return nsnull;
+ }
easier to breakpoint, and less compare/branch tests in the common case
One concern I have with this is that it will bloat in the case where
PREF_ClearAllUserPrefs() is called (when a "profile-do-change" message is
received for example.) There will be a similar, albeit smaller, bloat if a pref
or a pref branch is removed.
As a side note to this... pref_ClearUserPref() returns PL_DHASH_REMOVE when it
resets a user pref. Doesn't that cause the entry to be deleted? That will delete
the default information (if there is any) too...
And an unrelated note... I noticed that after the hashtable cleanup we now have
the function pref_HashTableEnumerateEntries() which basically calls
PL_DHashTableEnumerate()... not terribly efficient.
Assignee | ||
Comment 47•23 years ago
|
||
ok, I'll clean up whitespace (and good catch on 8196, I realized it after I
attached a patch... really!) and I like that gRoot branch cleanup
Now, I think we're ok as far as leaking/etc. We will bloat minorly because we
never free up pref name strings until shutdown, but personally I think its
worth possibly bloating a few k when the profile switches, than making lots of
little allocations. .. eventually we might want to blow everything away and
re-read the default prefs whenever a profile changes.
oh, and I cleaned up pref_HashTableEnumerateEntries while I was there.
Attachment #61441 -
Attachment is obsolete: true
Comment 48•23 years ago
|
||
Comment on attachment 61597 [details] [diff] [review]
use a real 8k buffer, with cleanups
I don't think you want to check in that makefile change...
I did some testing and, at least on the Mac, we never bloated, even during
profile migration, because it does currently blow away everthing and reload. I
really believe this is the right thing to do anyway... I don't see any real
gain from trying to only clear the user prefs.
Attachment #61597 -
Flags: review+
Comment 49•23 years ago
|
||
Er... that should have ended with r=bnesse.
Assignee | ||
Comment 50•23 years ago
|
||
brendan, can you take a look at the latest patch? its got r=bnesse, bug again I
want to make sure I'm taking the right approach here.
Whiteboard: fix in hand
Comment 51•23 years ago
|
||
Comment on attachment 61597 [details] [diff] [review]
use a real 8k buffer, with cleanups
>- PL_strfree((char*)pref->key);
>+ // pref->key is cleared by the global PrefNameBuffer
>+ pref->key = nsnull;
> memset(entry, 0, table->entrySize);
The comment confuses me -- does it mean that pref->key points to memory owned
by
the global PrefNameBuffer and freed all at once (at shutdown)?
>+char*
>+PrefNameBuffer::Alloc(PRInt32 len)
>+{
>+ NS_ASSERTION(this == gRoot, "Can only allocate on the root!\n");
>+ NS_ASSERTION(len < PREFNAME_BUFFER_LEN, "Can only allocate short strings\n");
>+ // check for space in the current buffer
>+ if ((mNextFree + len) > PREFNAME_BUFFER_LEN - 1) {
>+ // allocate and update the root
>+ gRoot = new PrefNameBuffer(this);
>+ return gRoot->Alloc(len);
>+ }
>+
>+ // ok, we have space.
>+ char *result = &mBuf[mNextFree];
>+ mNextFree += len;
>+
>+ return result;
>+}
Sure, may as well align allocations. Any word-copy-optimized memcpy will have
code (cf. Duff's Device) to unroll a byte-loop to align the copy, if necessary.
Bypassing that code wins in cycles, at the cost of ~2 bytes per string, average
(assuming uniformly distributed string lengths -- not sure that holds!).
>+const char *
>+PrefNameBuffer::StrDup(const char *str)
>+{
>+ if (!gRoot) {
>+ gRoot = new PrefNameBuffer(nsnull);
>+
>+ // ack, no memory
>+ if (!gRoot)
>+ return nsnull;
>+ }
>+
>+ // extra byte for null-termination
>+ PRInt32 len = PL_strlen(str) + 1;
>+
>+ char *buf = gRoot->Alloc(len);
>+ PL_strcpy(buf, str);
Use memcpy, you have the length in bytes to pass, and it's fastest in general
(inlined by gcc for constant len, possibly for short non-constant len). I do
not want to overhype it, so I'll say that another reason to use it is to get
with the standard C/C++ program to the extent that we can -- PL_strcpy is IIRC
a wrapper added only to work around some HPUX bug in 1996! We should use
strlen these days, I claim, unless you have the number of bytes precomputed,
as this code does, in which case memcpy is indicated.
>+ return buf;
>+}
>+
>+void
>+PrefNameBuffer::FreeAllBuffers()
>+{
>+ if (!gRoot) return;
>+ gRoot->FreeBuffer();
How deep are we gonna recurse here and in similar cases? Worth unwinding the
tail recursion (transforming to tail as needed)?
>+ gRoot = nsnull;
>+}
Looks good otherwise (I must need to review other patches in this bug, still).
What's the effect on startup time?
/be
Assignee | ||
Comment 52•23 years ago
|
||
this patch addresses brendan's comments.. brendan/brian, care to review?
(Brendan, all the other patches listed here are checked in, so I won't need
your review on those)
I wasn't sure of the best way to align the "next free" pointer, so I welcome
any suggestions.
Attachment #61597 -
Attachment is obsolete: true
Comment 53•23 years ago
|
||
Comment on attachment 63405 [details] [diff] [review]
same thing, and align on 4 byte boundaries
+void
+PrefNameBuffer::FreeBuffer()
+{
+ if (mNext)
+ mNext->FreeBuffer();
+ delete this;
+}
Isnt tail recursion supposed to be better ? something like:
PrefNameBuffer *next = mNext;
delete this;
next->FreeBuffer();
BTW, is delete this from a method ok ?
Attachment #63405 -
Flags: review+
Assignee | ||
Comment 54•23 years ago
|
||
ooh... I didn't understand what brendan meant, then promptly forgot about it.
now that I've seen it written out, I like it. I'll use dp's code.
Comment 55•23 years ago
|
||
Comment on attachment 63405 [details] [diff] [review]
same thing, and align on 4 byte boundaries
>+ PRInt32 mNextFree;
Use PRUint32, or cast when using as %-with-power-of-two constant operand (see
below).
>+ // check for space in the current buffer
>+ if ((mNextFree + len) > PREFNAME_BUFFER_LEN - 1) {
Why - 1? If mNextFree + len <= PREFNAME_BUFFER_LEN, then len <=
PREFNAME_BUFFER_LEN - mNextFree and the allocation fits in the current buffer.
>+ // now align the next free allocation
>+ if (mNextFree % PR_ALIGN_OF_WORD)
>+ mNextFree += (PR_ALIGN_OF_WORD - (mNextFree % PR_ALIGN_OF_WORD));
Given a signed integral type for mNextFree, % requires a sign test if it is
strength-reduced to &, as it should be here. So use PRUint32 for the member's
type, or at least cast here.
>+
>+ return result;
>+}
>+
>+void
>+PrefNameBuffer::FreeBuffer()
>+{
>+ if (mNext)
>+ mNext->FreeBuffer();
>+ delete this;
>+}
dp, by "(transforming to tail as needed)" I meant what you did. But that was
parenthetical to my main point, which was to manually eliminate recursion,
replacing it with iteration. No C/C++ compiler known to me will do that for
you. So:
void
PrefNameBuffer::FreeBuffer()
{
PrefNameBuffer* curr = this;
PrefNameBuffer* next;
do {
next = curr->mNext;
delete curr;
} while ((curr = next) != nsnull);
}
>+ // extra byte for null-termination
>+ PRInt32 len = PL_strlen(str) + 1;
Ack! My sermon on the obsolescence of PL_str* didn't electrify you enough :-).
Scour all PL_str*, I say. Just use strlen, etc.
/be
Assignee | ||
Comment 56•23 years ago
|
||
cool, thanks for the further reviews. will attach a (hoepfully final) patch later
Comment 57•23 years ago
|
||
Gilding the lily here on alignment fu.
>+ // now align the next free allocation
>+ if (mNextFree % PR_ALIGN_OF_WORD)
>+ mNextFree += (PR_ALIGN_OF_WORD - (mNextFree % PR_ALIGN_OF_WORD));
Besides being careful to avoid signed modulus overhead, you might explicitly
eliminate the common subexpression (mNextFree % PR_ALIGN_OF_WORD) with a local
variable. But see next point for a better way to avoid repetition or c.s.e.
An if is required only if mNextFree has a pointer type and you don't want ugly
casts. If the alignment modulus is a power of two (it had better be), and if
you use an integral-type (word-sized) cursor instead of a pointer-type cursor,
as you do here (PRUint32 in my suggested revision), then you can align without
the if-test and without any signed-modulus hassles, via :
mNextFree = (mNextFree + WORD_ALIGN_MASK) & ~WORD_ALIGN_MASK;
where WORD_ALIGN_MASK is (PR_ALIGN_OF_WORD - 1).
If you're worried about the return of 36-bits-per-word computers :-), then you
can raise a portability flag with:
#define WORD_ALIGN_MASK (PR_ALIGN_OF_WORD - 1)
#if (PR_ALIGN_OF_WORD & WORD_ALIGN_MASK) != 0
# error "PR_ALIGN_OF_WORD is not a power of two!"
#endif
(Proof that (n & (n - 1)) == 0 iff n is a power of two is left to the reader.)
So in summary:
* use PRUint32 for mNextFree;
* avoid %, use &~ as sketched above;
* insist that the modulus is a power of two when deriving its mask, just in case
something's fubared (not in case of 36-bit machine comebacks).
/be
Comment 58•23 years ago
|
||
The diffs for nsPrefBranch.cpp, nsPrefService.cpp, and prefapi_private_data.h are
missing... I assume they are still part of your patch?
And, as long as you're cleaning up...
+// making PrefNameBuffer exactly 8k for nice allocation
+#define PREFNAME_BUFFER_LEN (8192 - (sizeof(PrefNameBuffer*) + sizeof(char*)))
[snip]
+ // member variables
+ class PrefNameBuffer* mNext;
+ PRInt32 mNextFree;
+ char mBuf[PREFNAME_BUFFER_LEN];
Seems to me that the define should read...
+#define PREFNAME_BUFFER_LEN (8192 - (sizeof(PrefNameBuffer*) + sizeof(PRInt32)))
or ...sizeof(PRUint32) based on brendans comments.;)
Assignee | ||
Comment 59•23 years ago
|
||
excellent, thanks for the alignment help - I had finally figured out how to do
a non-branching align just before you posted that :)
Anyway, all commments addressed again. one more try, hopefully I got it right
this time :)
Attachment #63405 -
Attachment is obsolete: true
Comment 60•23 years ago
|
||
Comment on attachment 63577 [details] [diff] [review]
align on word boundary, take 2
Looks good, but now I'm wondering something that I should have asked earlier:
why not just use a PLArenaPool? It has slightly higher memory overhead
(because it has no constant PREFNAME_BUFFER_LEN analogue, it stores a limit
cursor in each arena), but using it instead of writing this code should save
code space (and development time :-/).
/be
Attachment #63577 -
Flags: superreview+
Assignee | ||
Comment 61•23 years ago
|
||
oh man, wish you had said that earlier.. :) I guess I still don't understand
when it's better to use arenas vs. your own mem management.
Oh well.. the code is pretty tiny for this use... I'll land as is and we can
clean up if it looks like we need it.
Comment 62•23 years ago
|
||
Comment on attachment 63577 [details] [diff] [review]
align on word boundary, take 2
>Index: prefapi.cpp
>===================================================================
>RCS file: /cvsroot/mozilla/modules/libpref/src/prefapi.cpp,v
>retrieving revision 3.112
>diff -u -r3.112 prefapi.cpp
>--- prefapi.cpp 21 Dec 2001 22:29:25 -0000 3.112
>+++ prefapi.cpp 4 Jan 2002 22:10:37 -0000
>@@ -176,8 +178,97 @@
> nsnull,
> };
>
>+class PrefNameBuffer;
>
>-/*----------------------------------------------------------------------------------------*/
>+// making PrefNameBuffer exactly 8k for nice allocation
>+#define PREFNAME_BUFFER_LEN (8192 - (sizeof(PrefNameBuffer*) + sizeof(char*)))
>+#define WORD_ALIGN_MASK (PR_ALIGN_OF_WORD - 1)
>+
>+// sanity checking
>+#if (PR_ALIGN_OF_WORD & WORD_ALIGN_MASK) != 0
>+#error "PR_ALIGN_OF_WORD must be a power of 2!"
>+#endif
Chris Seawood just reported that the use of PR_ALIGN_OF_WORD
breaks the Seamonkey-Ports Tinderboxes because this macro is
only defined on Mac, Win32, and Linux (bug 118286).
PR_ALIGN_OF_WORD is an undocumented macro that we keep around
for backward compatibility. It should not be used in new code.
I have two suggested fixes.
1. Be conservative and align on 8-byte boundaries. That is,
simply define WORD_ALIGN_MASK as 0x7.
2. Replace PR_ALIGN_OF_WORD by sizeof(void *).
In either case, the sanity checking will need to be deleted.
In fact, since you only use PrefNameBuffer::Alloc to allocate
character strings, I don't understand why you need to align
at all.
Attachment #63577 -
Flags: needs-work+
Comment 63•23 years ago
|
||
OK, I saw that you want to align the character strings to
improve the performance of strcmp. :-)
Comment 64•23 years ago
|
||
The patch busted darwin...
prefapi.cpp
c++ -o prefapi.o -c -DOSTYPE=\"Darwin5.2\" -DOSARCH=\"Darwin\" -DOJI
-DB_ONE_M -I../../../dist/include/xpcom -I../../../dist/include/string -I../../../
dist/include/js -I../../../dist/include/xpconnect -I../../../dist/include/caps -I../../../
dist/include/intl -I../../../dist/include/necko -I../../../dist/include/pref -I../../../dist/
include -I/Users/josh/Code/Mozilla_Project/mozilla/dist/include/nspr -
fPIC -fno-rtti -fno-exceptions -Wall -Wconversion -Wpointer-arith -Wbad-
function-cast -Wcast-align -Woverloaded-virtual -Wsynth -Wno-ctor-dtor-
privacy -Wno-long-long -fpascal-strings -traditional-cpp -fno-common -I/
Developer/Headers/FlatCarbon -F/System/Library/Frameworks -pipe -
DNDEBUG -DTRIMMED -O -DMOZILLA_CLIENT -include ../../../config-
defs.h -Wp,-MD,.deps/prefapi.pp prefapi.cpp
prefapi.cpp: In method `char * PrefNameBuffer::Alloc(int)':
prefapi.cpp:231: `PR_ALIGN_OF_WORD' undeclared (first use this
function)
prefapi.cpp:231: (Each undeclared identifier is reported only once
prefapi.cpp:231: for each function it appears in.)
nsPrefBranch.cpp: In method `nsresult nsPrefBranch::GetChildList(const
char *, PRUint32 *, char ***)':
nsPrefBranch.cpp:525: warning: unused variable `nsresult rv'
nsPrefBranch.cpp: In method `nsresult nsPrefBranch::AddObserver(const
char *, nsIObserver *, int)':
nsPrefBranch.cpp:584: warning: unused variable `nsresult rv'
nsPrefBranch.cpp: In method `nsresult
nsPrefBranch::RemoveObserver(const char *, nsIObserver *)':
nsPrefBranch.cpp:625: warning: `struct PrefCallbackData * pCallback'
might be used uninitialized in this function
../../../dist/include/string/nsBufferHandle.h: At top level:
prefapi.cpp:165: warning: `char * gLockFileName' defined but not used
prefapi.cpp:166: warning: `char * gLockVendor' defined but not used
prefapi.cpp:305: warning: `PRBool pref_VerifyLockFile(char *, long int)'
defined but not used
prefapi.cpp:1254: warning: `enum PrefResult
PREF_CreateChildList(const char *, char **)' defined but not used
make[4]: *** [prefapi.o] Error 1
make[4]: *** Waiting for unfinished jobs....
make[4]: Leaving directory `/Users/josh/Code/Mozilla_Project/mozilla/
modules/libpref/src'
make[3]: *** [libs] Error 2
make[3]: Leaving directory `/Users/josh/Code/Mozilla_Project/mozilla/
modules/libpref'
make[2]: *** [tier_9] Error 2
make[2]: Leaving directory `/Users/josh/Code/Mozilla_Project/mozilla'
make[1]: *** [default] Error 2
make[1]: Leaving directory `/Users/josh/Code/Mozilla_Project/mozilla'
make: *** [build] Error 2
make: Leaving directory `/Users/josh/Code/Mozilla_Project/mozilla'
Assignee | ||
Comment 65•23 years ago
|
||
I had already checked in that patch... I fixed the bustage by defining my own
ALIGN_OF_WORD as sizeof(void*) on platforms where it isn't already defined.
That completes this bug. For further malloc issues, file new bugs.
Status: ASSIGNED → RESOLVED
Closed: 23 years ago
Resolution: --- → FIXED
Comment 66•23 years ago
|
||
Alec Flett wrote:
> I fixed the bustage by defining my own ALIGN_OF_WORD
> as sizeof(void*) on platforms where it isn't already defined.
I reiterate my recommendation that PR_ALIGN_OF_WORD should not
be used in new code. You should just use sizeof(void*) on all
platforms.
You need to log in
before you can comment on or make changes to this bug.
Description
•