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. (#5017)

* Refactor type map to have a separate flag field for each type instead of magic location in the array.

Refactor the type map to have a separate flag field for each type instead of a
magic location in the array. This simplifies some code, but also allows us to
introduce more flags in the future (which becomes increasingly complex if the
location in the array is to determine yet another value).

This has neglible performance impacts.

* Add a UserType flag for registered types, and use it to improve the performance 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 somehow made the dontlink
scenario so fast that it's faster than the linkall scenario (which also
improved, but not nearly as much). While quite inexplicable, it's a consistent
result I've seen over multiple test runs.

Numbers
=======

Test case: rolfbjarne/TestApp@004283d

Fix 1 refers to PR #5009.
Fix 2 refers to PR #5013.
Fix 3 refers to PR #5016.
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 (the last) of multiple fixes for #4936.

The total speed-up is 69-86% (3-7x faster).
  • Loading branch information
rolfbjarne committed Oct 22, 2018
1 parent 097e3fc commit c378d6b
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 41 deletions.
42 changes: 37 additions & 5 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,14 +1014,15 @@ -(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 - map->custom_type_count, sizeof (MTClassMap), compare_mtclassmap);
qsort (&map->map [map->map_count - map->custom_type_count], map->custom_type_count, sizeof (MTClassMap), compare_mtclassmap);
qsort (map->map, map->map_count, sizeof (MTClassMap), compare_mtclassmap);
}

/*
Expand Down Expand Up @@ -1895,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
10 changes: 8 additions & 2 deletions runtime/xamarin/runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,16 @@ typedef struct __attribute__((packed)) {
} MTTokenReference;
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)) {
void *handle;
uint32_t /* MTTokenReference */ type_reference;
uint32_t /* MTTypeFlags */ flags;
} MTClassMap;

typedef struct __attribute__((packed)) {
Expand Down Expand Up @@ -109,7 +116,6 @@ struct MTRegistrationMap {
const MTProtocolMap protocols;
int assembly_count;
int map_count;
int custom_type_count;
int full_token_reference_count;
int skipped_map_count;
int protocol_wrapper_count;
Expand Down Expand Up @@ -198,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
11 changes: 4 additions & 7 deletions src/ObjCRuntime/Class.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ unsafe static IntPtr FindClass (Type type, out bool is_custom_type)
continue;

var rv = map->map [i].handle;
is_custom_type = i >= (map->map_count - map->custom_type_count);
is_custom_type = (map->map [i].flags & Runtime.MTTypeFlags.CustomType) == Runtime.MTTypeFlags.CustomType;
#if LOG_TYPELOAD
Console.WriteLine ($"FindClass ({type.FullName}, {is_custom_type}): 0x{rv.ToString ("x")} = {class_getName (rv)}.");
#endif
Expand Down Expand Up @@ -331,19 +331,16 @@ internal unsafe static Type FindType (IntPtr @class, out bool is_custom_type)
}

// Find the ObjC class pointer in our map
var mapIndex = FindMapIndex (map->map, 0, map->map_count - map->custom_type_count - 1, @class);
if (mapIndex == -1) {
mapIndex = FindMapIndex (map->map, map->map_count - map->custom_type_count, map->map_count - 1, @class);
is_custom_type = true;
}

var mapIndex = FindMapIndex (map->map, 0, map->map_count - 1, @class);
if (mapIndex == -1) {
#if LOG_TYPELOAD
Console.WriteLine ($"FindType (0x{@class:X} = {Marshal.PtrToStringAuto (class_getName (@class))}) => found no type.");
#endif
return null;
}

is_custom_type = (map->map [mapIndex].flags & Runtime.MTTypeFlags.CustomType) == Runtime.MTTypeFlags.CustomType;

Type type = class_to_type [mapIndex];
if (type != null)
return type;
Expand Down
12 changes: 10 additions & 2 deletions src/ObjCRuntime/Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,25 @@ internal unsafe struct MTRegistrationMap {
public MTProtocolMap protocol_map;
public int assembly_count;
public int map_count;
public int custom_type_count;
public int full_token_reference_count;
public int skipped_map_count;
public int protocol_wrapper_count;
public int protocol_count;
}

[Flags]
internal enum MTTypeFlags : uint
{
None = 0,
CustomType = 1,
UserType = 2,
}

[StructLayout (LayoutKind.Sequential, Pack = 1)]
internal struct MTClassMap {
public IntPtr handle;
public uint type_reference;
public MTTypeFlags flags;
}

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

[Flags]
internal enum InitializationFlags : int {
/* unused = 0x01 */
IsPartialStaticRegistrar= 0x01,
/* unused = 0x02,*/
DynamicRegistrar = 0x04,
/* unused = 0x08,*/
Expand Down
43 changes: 18 additions & 25 deletions tools/common/StaticRegistrar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2725,48 +2725,34 @@ void Specialize (AutoIndentStringBuilder sb)
allTypes.Add (@class);
}

// Move all the custom types to the end of the list, respecting
// existing order (so that a derived type always comes after
// its base type; the Types.Values has that property, and we
// need to keep it that way).

var mappedEnd = allTypes.Count;
var counter = 0;
while (counter < mappedEnd) {
if (!IsPlatformType (allTypes [counter].Type)) {
var t = allTypes [counter];
allTypes.RemoveAt (counter);
allTypes.Add (t);
mappedEnd--;
} else {
counter++;
}
}

if (string.IsNullOrEmpty (single_assembly)) {
foreach (var assembly in GetAssemblies ())
registered_assemblies.Add (GetAssemblyName (assembly));
} else {
registered_assemblies.Add (single_assembly);
}

var customTypeCount = 0;
foreach (var @class in allTypes) {
var isPlatformType = IsPlatformType (@class.Type);
var flags = MTTypeFlags.None;

skip.Clear ();

uint token_ref = uint.MaxValue;
if (!@class.IsProtocol && !@class.IsCategory) {
if (!isPlatformType)
customTypeCount++;

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}' */ }},",
map.AppendLine ("{{ NULL, 0x{1:X} /* #{3} '{0}' => '{2}' */, (MTTypeFlags) ({4}) /* {5} */ }},",
@class.ExportedName,
CreateTokenReference (@class.Type, TokenType.TypeDef),
GetAssemblyQualifiedName (@class.Type), map_entries);
GetAssemblyQualifiedName (@class.Type), map_entries,
(int) flags, flags);
map_dict [@class] = map_entries++;

bool use_dynamic;
Expand Down Expand Up @@ -3110,15 +3096,14 @@ void Specialize (AutoIndentStringBuilder sb)
}
map.AppendLine ("{0},", count);
map.AppendLine ("{0},", i);
map.AppendLine ("{0},", customTypeCount);
map.AppendLine ("{0},", full_token_reference_count);
map.AppendLine ("{0},", skipped_types.Count);
map.AppendLine ("{0},", protocol_wrapper_map.Count);
map.AppendLine ("{0}", needs_protocol_map ? protocols.Count : 0);
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 @@ -5005,4 +4990,12 @@ class AdoptsAttribute : Attribute
{
public string ProtocolType { get; set; }
}

[Flags]
internal enum MTTypeFlags : uint
{
None = 0,
CustomType = 1,
UserType = 2,
}
}

0 comments on commit c378d6b

Please sign in to comment.