Implement O(1) interface lookups

Currently interface lookups are do a binary search over all the interfaces
an object implements. Its possible to do this lookup in constant time using for
instance the gcj algorighm described at:
http://gcc.gnu.org/ml/java/1999-q3/msg00377.html

This is an implementation of that based on GAtomicArray.
This commit is contained in:
Alexander Larsson
2009-09-09 16:42:32 +02:00
parent 8f27a5e621
commit 69961d27a1

View File

@@ -245,6 +245,7 @@ struct _TypeNode
#define SIZEOF_BASE_TYPE_NODE() (G_STRUCT_OFFSET (TypeNode, supers)) #define SIZEOF_BASE_TYPE_NODE() (G_STRUCT_OFFSET (TypeNode, supers))
#define MAX_N_SUPERS (255) #define MAX_N_SUPERS (255)
#define MAX_N_CHILDREN (4095) #define MAX_N_CHILDREN (4095)
#define MAX_N_INTERFACES (255) /* Limited by offsets being 8 bits */
#define MAX_N_PREREQUISITES (511) #define MAX_N_PREREQUISITES (511)
#define NODE_TYPE(node) (node->supers[0]) #define NODE_TYPE(node) (node->supers[0])
#define NODE_PARENT_TYPE(node) (node->supers[1]) #define NODE_PARENT_TYPE(node) (node->supers[1])
@@ -281,7 +282,7 @@ struct _IFaceEntry
}; };
struct _IFaceEntries { struct _IFaceEntries {
guint offset_index; /* not used yet */ guint offset_index;
IFaceEntry entry[1]; IFaceEntry entry[1];
}; };
@@ -304,6 +305,7 @@ struct _IFaceData
GClassFinalizeFunc dflt_finalize; GClassFinalizeFunc dflt_finalize;
gconstpointer dflt_data; gconstpointer dflt_data;
gpointer dflt_vtable; gpointer dflt_vtable;
GAtomicArray offsets;
}; };
struct _ClassData struct _ClassData
@@ -528,36 +530,44 @@ type_node_new_W (TypeNode *pnode,
} }
static inline IFaceEntry* static inline IFaceEntry*
lookup_iface_entry_I (IFaceEntries *entries, lookup_iface_entry_I (volatile IFaceEntries *entries,
TypeNode *iface_node) TypeNode *iface_node)
{ {
if (entries != NULL) guint8 *offsets;
{ guint offset_index;
IFaceEntry *ifaces = &entries->entry[-1]; IFaceEntry *check;
guint n_ifaces = IFACE_ENTRIES_N_ENTRIES (entries); int index;
GType iface_type = NODE_TYPE (iface_node); IFaceEntry *entry;
do if (entries == NULL)
{ return NULL;
guint i;
IFaceEntry *check; G_ATOMIC_ARRAY_DO_TRANSACTION
(&iface_node->data->iface.offsets, guint8,
i = (n_ifaces + 1) >> 1;
check = ifaces + i; entry = NULL;
if (iface_type == check->iface_type) offsets = transaction_data;
return check; offset_index = entries->offset_index;
else if (iface_type > check->iface_type) if (offsets != NULL &&
{ offset_index < G_ATOMIC_ARRAY_DATA_SIZE(offsets))
n_ifaces -= i; {
ifaces = check; index = offsets[offset_index];
} if (index > 0)
else /* if (iface_type < check->iface_type) */ {
n_ifaces = i - 1; /* zero means unset, subtract one to get real index */
} index -= 1;
while (n_ifaces);
} if (index < IFACE_ENTRIES_N_ENTRIES (entries))
{
return NULL; check = (IFaceEntry *)&entries->entry[index];
if (check->iface_type == NODE_TYPE (iface_node))
entry = check;
}
}
}
);
return entry;
} }
static inline IFaceEntry* static inline IFaceEntry*
@@ -1215,6 +1225,88 @@ type_data_unref_WmREC (TypeNode *node,
} }
} }
static gboolean
iface_node_has_available_offset_L (TypeNode *iface_node,
int offset,
int for_index)
{
guint8 *offsets;
offsets = G_ATOMIC_ARRAY_GET_LOCKED (&iface_node->data->iface.offsets, guint8);
if (offsets == NULL)
return TRUE;
if (G_ATOMIC_ARRAY_DATA_SIZE (offsets) <= offset)
return TRUE;
if (offsets[offset] == 0 ||
offsets[offset] == for_index+1)
return TRUE;
return FALSE;
}
static int
find_free_iface_offset_L (IFaceEntries *entries)
{
IFaceEntry *entry;
TypeNode *iface_node;
int offset;
int i;
int n_entries;
n_entries = IFACE_ENTRIES_N_ENTRIES (entries);
offset = -1;
do
{
offset++;
for (i = 0; i < n_entries; i++)
{
entry = &entries->entry[i];
iface_node = lookup_type_node_I (entry->iface_type);
if (!iface_node_has_available_offset_L (iface_node, offset, i))
break;
}
}
while (i != n_entries);
return offset;
}
static void
iface_node_set_offset_L (TypeNode *iface_node,
int offset,
int index)
{
guint8 *offsets, *old_offsets;
int new_size, old_size;
int i;
old_offsets = G_ATOMIC_ARRAY_GET_LOCKED (&iface_node->data->iface.offsets, guint8);
if (old_offsets == NULL)
old_size = 0;
else
{
old_size = G_ATOMIC_ARRAY_DATA_SIZE (old_offsets);
if (offset < old_size &&
old_offsets[offset] == index + 1)
return; /* Already set to this index, return */
}
new_size = MAX (old_size, offset + 1);
offsets = _g_atomic_array_copy (&iface_node->data->iface.offsets,
0, new_size - old_size);
/* Mark new area as unused */
for (i = old_size; i < new_size; i++)
offsets[i] = 0;
offsets[offset] = index + 1;
_g_atomic_array_update (&iface_node->data->iface.offsets, offsets);
}
static void static void
type_node_add_iface_entry_W (TypeNode *node, type_node_add_iface_entry_W (TypeNode *node,
GType iface_type, GType iface_type,
@@ -1222,17 +1314,19 @@ type_node_add_iface_entry_W (TypeNode *node,
{ {
IFaceEntries *entries; IFaceEntries *entries;
IFaceEntry *entry; IFaceEntry *entry;
guint i; TypeNode *iface_node;
guint i, j;
int num_entries; int num_entries;
g_assert (node->is_instantiatable); g_assert (node->is_instantiatable);
entries = CLASSED_NODE_IFACES_ENTRIES_LOCKED (node); entries = CLASSED_NODE_IFACES_ENTRIES_LOCKED (node);
i = 0;
if (entries != NULL) if (entries != NULL)
{ {
num_entries = IFACE_ENTRIES_N_ENTRIES (entries); num_entries = IFACE_ENTRIES_N_ENTRIES (entries);
g_assert (num_entries < MAX_N_INTERFACES);
for (i = 0; i < num_entries; i++) for (i = 0; i < num_entries; i++)
{ {
entry = &entries->entry[i]; entry = &entries->entry[i];
@@ -1256,8 +1350,6 @@ type_node_add_iface_entry_W (TypeNode *node,
} }
return; return;
} }
else if (entry->iface_type > iface_type)
break;
} }
} }
@@ -1265,8 +1357,9 @@ type_node_add_iface_entry_W (TypeNode *node,
IFACE_ENTRIES_HEADER_SIZE, IFACE_ENTRIES_HEADER_SIZE,
sizeof (IFaceEntry)); sizeof (IFaceEntry));
num_entries = IFACE_ENTRIES_N_ENTRIES (entries); num_entries = IFACE_ENTRIES_N_ENTRIES (entries);
g_memmove (&entries->entry[i + 1], &entries->entry[i], i = num_entries - 1;
sizeof (IFaceEntry) * (num_entries - i - 1)); if (i == 0)
entries->offset_index = 0;
entries->entry[i].iface_type = iface_type; entries->entry[i].iface_type = iface_type;
entries->entry[i].vtable = NULL; entries->entry[i].vtable = NULL;
entries->entry[i].init_state = UNINITIALIZED; entries->entry[i].init_state = UNINITIALIZED;
@@ -1280,6 +1373,30 @@ type_node_add_iface_entry_W (TypeNode *node,
} }
} }
/* Update offsets in iface */
iface_node = lookup_type_node_I (iface_type);
if (iface_node_has_available_offset_L (iface_node,
entries->offset_index,
i))
{
iface_node_set_offset_L (iface_node,
entries->offset_index, i);
}
else
{
entries->offset_index =
find_free_iface_offset_L (entries);
for (j = 0; j < IFACE_ENTRIES_N_ENTRIES (entries); j++)
{
entry = &entries->entry[j];
iface_node =
lookup_type_node_I (entry->iface_type);
iface_node_set_offset_L (iface_node,
entries->offset_index, j);
}
}
_g_atomic_array_update (CLASSED_NODE_IFACES_ENTRIES (node), entries); _g_atomic_array_update (CLASSED_NODE_IFACES_ENTRIES (node), entries);
if (parent_entry) if (parent_entry)