On Sloan's Sun E450 (not a terribly overwhelmingly fast machine by today's standards, a bit of a slug really), with 11,000 users, a couple of thousand groups, a half-million objects ... calls to permission_p are running from 6 to 10 milliseconds. That's pretty minimal for random checks on permissions for a particular object.
Using the acs_object_party_privilege_map directly in the WHERE clause is a much faster way to find out the objects you have permission to view in a particular content folder or under a particular package. So fast that I'd much rather see the bookmarks and similar packages continue to use permissions, switching to private_p wouldn't really be particularly faster. The extra flexibility could be useful for someone customizing the package - for instance you could make the bookmarks group-private rather than individual-private just by fiddling the perms rather than fiddling code extensively.
Here's an answer I e-mailed Rocael earlier (he originally e-mailed his question to me and Jeff Davis spanked us for not having this discussion in public):
You are on the right track, very close to being right actually, you're only missing the fact that you should use the acs_object_party_privilege_map view for such checking.
This used to do the UNION of groups, parties, relational segments that you're describing as missing above. My changes in 4.6.1 creates a (large) denormalized table that maintains the same state as an explicit UNION, vastly speeding up the view especially (but not only) in PostgreSQL.
Here's a query snippet (from a big gross query in file storage) that should get the idea across:
select fs_urls_full.url_id as object_id,
acs_permission__permission_p(fs_urls.url_id, :viewing_user_id, 'write') as write_p,
acs_permission__permission_p(fs_urls.url_id, :viewing_user_id, 'delete') as delete_p,
1 as sort_key
from fs_urls_full
where fs_urls_full.folder_id = :folder_id
and exists (select 1
from acs_object_party_privilege_map m
where m.object_id = fs_urls_full.url_id
and m.party_id = :viewing_user_id
and m.privilege = 'read')) as fs_objects
Note the use of acs_permission__permission_p() to determine write and delete privileges on the rows that are actually returned but the use of
the acs_object_party_privilege_map in the WHERE clause to figure out which rows to process and return. Switching to the use of the view in the WHERE clause sped things up by a factor of two or three even though the query had previously been optimized to minimize the calls to permission_p.
The EXISTS subquery you see there works regardless of whether the user has the permission directly, due to being a member of a group, or a relational segment.
In particular it is no longer necessary to ever check for "user_id = 0 and permisssion granted on group -1" or stuff like that again. Just the direct check against this view is sufficient and covers all the cases.