Skip to content

Commit

Permalink
Optimize startup be reducing selector lookup.
Browse files Browse the repository at this point in the history
Move away from looking up selectors at proto initialization time to doing optimized string compares at implementation resolving time.

PiperOrigin-RevId: 623183331
  • Loading branch information
Dave MacLachlan authored and Copybara-Service committed Apr 9, 2024
1 parent e22539b commit b375d01
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 122 deletions.
55 changes: 1 addition & 54 deletions objectivec/GPBDescriptor.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,40 +51,6 @@ - (instancetype)initWithName:(NSString *)name
static const char kClassNameSuffixKey = 0;
static const char kFileDescriptorCacheKey = 0;

// Utility function to generate selectors on the fly.
static SEL SelFromStrings(const char *prefix, const char *middle, const char *suffix,
BOOL takesArg) {
if (prefix == NULL && suffix == NULL && !takesArg) {
return sel_getUid(middle);
}
const size_t prefixLen = prefix != NULL ? strlen(prefix) : 0;
const size_t middleLen = strlen(middle);
const size_t suffixLen = suffix != NULL ? strlen(suffix) : 0;
size_t totalLen = prefixLen + middleLen + suffixLen + 1; // include space for null on end.
if (takesArg) {
totalLen += 1;
}
char buffer[totalLen];
if (prefix != NULL) {
memcpy(buffer, prefix, prefixLen);
memcpy(buffer + prefixLen, middle, middleLen);
buffer[prefixLen] = (char)toupper(buffer[prefixLen]);
} else {
memcpy(buffer, middle, middleLen);
}
if (suffix != NULL) {
memcpy(buffer + prefixLen + middleLen, suffix, suffixLen);
}
if (takesArg) {
buffer[totalLen - 2] = ':';
}
// Always null terminate it.
buffer[totalLen - 1] = 0;

SEL result = sel_getUid(buffer);
return result;
}

static NSArray *NewFieldsArrayForHasIndex(int hasIndex, NSArray *allMessageFields)
__attribute__((ns_returns_retained));

Expand Down Expand Up @@ -594,8 +560,6 @@ - (instancetype)initWithName:(const char *)name fields:(NSArray *)fields {
for (GPBFieldDescriptor *fieldDesc in fields) {
fieldDesc->containingOneof_ = self;
}

caseSel_ = SelFromStrings(NULL, name, "OneOfCase", NO);
}
return self;
}
Expand Down Expand Up @@ -682,27 +646,9 @@ - (instancetype)initWithFieldDescription:(void *)description
coreDesc = description;
}
description_ = coreDesc;
getSel_ = sel_getUid(coreDesc->name);
setSel_ = SelFromStrings("set", coreDesc->name, NULL, YES);

GPBDataType dataType = coreDesc->dataType;
BOOL isMessage = GPBDataTypeIsMessage(dataType);
BOOL isMapOrArray = GPBFieldIsMapOrArray(self);

if (isMapOrArray) {
// map<>/repeated fields get a *Count property (inplace of a has*) to
// support checking if there are any entries without triggering
// autocreation.
hasOrCountSel_ = SelFromStrings(NULL, coreDesc->name, "_Count", NO);
} else {
// It is a single field; it gets has/setHas selectors if...
// - not in a oneof (negative has index)
// - not clearing on zero
if ((coreDesc->hasIndex >= 0) && ((coreDesc->flags & GPBFieldClearHasIvarOnZero) == 0)) {
hasOrCountSel_ = SelFromStrings("has", coreDesc->name, NULL, NO);
setHasSel_ = SelFromStrings("setHas", coreDesc->name, NULL, YES);
}
}

// Extra type specific data.
if (isMessage) {
Expand All @@ -719,6 +665,7 @@ - (instancetype)initWithFieldDescription:(void *)description
}

// Non map<>/repeated fields can have defaults in proto2 syntax.
BOOL isMapOrArray = GPBFieldIsMapOrArray(self);
if (!isMapOrArray && includesDefault) {
defaultValue_ = ((GPBMessageFieldDescriptionWithDefault *)description)->defaultValue;
if (dataType == GPBDataTypeBytes) {
Expand Down
10 changes: 4 additions & 6 deletions objectivec/GPBDescriptor_PackagePrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ typedef struct GPBFileDescription {
// Describes a single field in a protobuf as it is represented as an ivar.
typedef struct GPBMessageFieldDescription {
// Name of ivar.
// Note that we looked into using a SEL here instead (which really is just a C string)
// but there is not a way to initialize an SEL with a constant (@selector is not constant) so the
// additional code generated to initialize the value is actually bigger in size than just using a
// C identifier for large apps.
const char *name;
union {
// className is deprecated and will be removed in favor of clazz.
Expand Down Expand Up @@ -235,7 +239,6 @@ typedef NS_OPTIONS(uint32_t, GPBDescriptorInitializationFlags) {
@package
const char *name_;
NSArray *fields_;
SEL caseSel_;
}
// name must be long lived.
- (instancetype)initWithName:(const char *)name fields:(NSArray *)fields;
Expand All @@ -245,11 +248,6 @@ typedef NS_OPTIONS(uint32_t, GPBDescriptorInitializationFlags) {
@package
GPBMessageFieldDescription *description_;
GPB_UNSAFE_UNRETAINED GPBOneofDescriptor *containingOneof_;

SEL getSel_;
SEL setSel_;
SEL hasOrCountSel_; // *Count for map<>/repeated fields, has* otherwise.
SEL setHasSel_;
}
@end

Expand Down
Loading

0 comments on commit b375d01

Please sign in to comment.