Skip to content

Commit

Permalink
Add a UserType flag for registered types, and use it to improve the p…
Browse files Browse the repository at this point in the history
…erformance for is_user_type.

Reflection in the Objective-C runtime is apparently quite slow, so try to
avoid it by computing the information we need for determining whether a
particular Objective-C type represents a user type or not in the static
registrar.

We store this information in a flag for the type in question in the type map,
and use a binary search to search the type map when needed.

This provides a significant improvement, in particular in the dontlink
scenario (probably because there are many more Objective-C types in the app,
which made Objective-C reflection slow). In fact, it made the dontlink
scenario so fast that it's faster than the linkall scenario (which also
improved, but not nearly as much).

Numbers
=======

Test case: rolfbjarne/TestApp@004283d

Fix 1 refers to PR xamarin#5009.
Fix 2 refers to PR xamarin#5013.
Fix 3 refers to PR #?
Fix 4 is this fix.

iPad Air 2
----------

| Configuration       | Before | After fix 1 | After fix 2  | After fix 3  | After fix 4  | Improvement from fix 3 to fix 4 | Cumulative improvement |
| ------------------- | ------ | ----------: | -----------: | -----------: | -----------: | ------------------------------: | ---------------------: |
| Release (link all)  | 477 ms |      481 ms |       224 ms |       172 ms |       148 ms |                     24 ms (14%) |           329 ms (69%) |
| Release (dont link) | 738 ms |      656 ms |       377 ms |       201 ms |       146 ms |                     55 ms (27%) |           592 ms (80%) |

iPhone X
--------

| Configuration       | Before | After fix 1 | After fix 2  | After fix 3  | After fix 4  | Improvement from fix 3 to fix 4 | Cumulative improvement |
| ------------------- | ------ | ----------: | -----------: | -----------: | -----------: | ------------------------------: | ---------------------: |
| Release (link all)  |  98 ms |       99 ms |        42 ms |        31 ms |        29 ms |                      2 ms ( 6%) |            69 ms (70%) |
| Release (dont link) | 197 ms |      153 ms |        91 ms |        43 ms |        28 ms |                     15 ms (35%) |           169 ms (86%) |

When linking all assemblies, the type map has 24 entries, and when not linking
at all it has 2993 entries.

This is part 4 of multiple fixes for xamarin#4936.

This is also the last fix; now the heaviest stack trace is SGen allocating
managed memory (and no other obvious low-hanging fruit either).

The total speed-up is 69-86% (3-7x faster).
  • Loading branch information
rolfbjarne committed Oct 19, 2018
1 parent 7b872a2 commit 46a242c
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 6 deletions.
39 changes: 36 additions & 3 deletions runtime/runtime.m
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
};

enum InitializationFlags : int {
/* unused = 0x01,*/
InitializationFlagsIsPartialStaticRegistrar = 0x01,
/* unused = 0x02,*/
InitializationFlagsDynamicRegistrar = 0x04,
/* unused = 0x08,*/
Expand Down Expand Up @@ -1014,10 +1014,12 @@ -(void) xamarinSetGCHandle: (int) gc_handle;
}

void
xamarin_add_registration_map (struct MTRegistrationMap *map)
xamarin_add_registration_map (struct MTRegistrationMap *map, bool partial)
{
// COOP: no managed memory access: any mode
options.RegistrationData = map;
if (partial)
options.flags = (InitializationFlags) (options.flags | InitializationFlagsIsPartialStaticRegistrar);

// Sort the type map according to Class
qsort (map->map, map->map_count, sizeof (MTClassMap), compare_mtclassmap);
Expand Down Expand Up @@ -1894,11 +1896,42 @@ -(void) xamarinSetGCHandle: (int) gc_handle;
set_raw_gchandle (self, gchandle);
}

static int
find_user_type_index (MTClassMap *map, int lo, int hi, Class cls)
{
if (hi >= lo) {
int mid = lo + (hi - lo) / 2;

if (map [mid].handle == cls)
return mid;

if ((intptr_t) map [mid].handle > (intptr_t) cls)
return find_user_type_index (map, lo, mid - 1, cls);

return find_user_type_index (map, mid + 1, hi, cls);
}

return -1;
}

static inline bool
is_user_type (id self)
{
Class cls = object_getClass (self);

if (options.RegistrationData != NULL && options.RegistrationData->map_count > 0) {
MTClassMap *map = options.RegistrationData->map;
int idx = find_user_type_index (map, 0, options.RegistrationData->map_count - 1, cls);
if (idx >= 0)
return (map [idx].flags & MTTypeFlagsUserType) == MTTypeFlagsUserType;
// If using the partial static registrar, we need to continue
// If full static registrar, we can return false
if ((options.flags & InitializationFlagsIsPartialStaticRegistrar) != InitializationFlagsIsPartialStaticRegistrar)
return false;
}

// COOP: no managed memory access: any mode
return class_getInstanceMethod (object_getClass (self), @selector (xamarinSetGCHandle:)) != NULL;
return class_getInstanceMethod (cls, @selector (xamarinSetGCHandle:)) != NULL;
}

#if defined(DEBUG_REF_COUNTING)
Expand Down
3 changes: 2 additions & 1 deletion runtime/xamarin/runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ static const uint32_t INVALID_TOKEN_REF = 0xFFFFFFFF;
enum MTTypeFlags {
MTTypeFlagsNone = 0,
MTTypeFlagsCustomType = 1, // not a platform type
MTTypeFlagsUserType = 2, // not a wrapped type
};

typedef struct __attribute__((packed)) {
Expand Down Expand Up @@ -203,7 +204,7 @@ void xamarin_unhandled_exception_handler (MonoObject *exc, gpointer user_data)
void xamarin_ftnptr_exception_handler (guint32 gchandle);
void xamarin_create_classes ();
const char * xamarin_skip_encoding_flags (const char *encoding);
void xamarin_add_registration_map (struct MTRegistrationMap *map);
void xamarin_add_registration_map (struct MTRegistrationMap *map, bool partial);
uint32_t xamarin_find_protocol_wrapper_type (uint32_t token_ref);
void xamarin_release_block_on_main_thread (void *obj);

Expand Down
3 changes: 2 additions & 1 deletion src/ObjCRuntime/Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ internal enum MTTypeFlags : uint
{
None = 0,
CustomType = 1,
UserType = 2,
}

[StructLayout (LayoutKind.Sequential, Pack = 1)]
Expand Down Expand Up @@ -131,7 +132,7 @@ internal struct Trampolines {

[Flags]
internal enum InitializationFlags : int {
/* unused = 0x01 */
IsPartialStaticRegistrar= 0x01,
/* unused = 0x02,*/
DynamicRegistrar = 0x04,
/* unused = 0x08,*/
Expand Down
6 changes: 5 additions & 1 deletion tools/common/StaticRegistrar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2743,6 +2743,9 @@ void Specialize (AutoIndentStringBuilder sb)
if (!isPlatformType)
flags |= MTTypeFlags.CustomType;

if (!@class.IsWrapper && !@class.IsModel)
flags |= MTTypeFlags.UserType;

CheckNamespace (@class, exceptions);
token_ref = CreateTokenReference (@class.Type, TokenType.TypeDef);
map.AppendLine ("{{ NULL, 0x{1:X} /* #{3} '{0}' => '{2}' */, (MTTypeFlags) ({4}) /* {5} */ }},",
Expand Down Expand Up @@ -3100,7 +3103,7 @@ void Specialize (AutoIndentStringBuilder sb)
map.AppendLine ("};");


map_init.AppendLine ("xamarin_add_registration_map (&__xamarin_registration_map);");
map_init.AppendLine ("xamarin_add_registration_map (&__xamarin_registration_map, {0});", string.IsNullOrEmpty (single_assembly) ? "false" : "true");
map_init.AppendLine ("}");

sb.WriteLine (map.ToString ());
Expand Down Expand Up @@ -4993,5 +4996,6 @@ internal enum MTTypeFlags : uint
{
None = 0,
CustomType = 1,
UserType = 2,
}
}

0 comments on commit 46a242c

Please sign in to comment.