मेरे लिए यह आमतौर पर प्रदर्शन है। किसी ऑब्जेक्ट का आइवर एक्सेस करना सी में एक स्ट्रक्चर मेंबर को एक्सेस करने के लिए उतनी ही तेजी से होता है, जितना कि इस तरह के स्ट्रक्चर वाले मेमोरी को पॉइंटर का इस्तेमाल करना। वास्तव में, ऑब्जेक्टिव-सी ऑब्जेक्ट मूल रूप से सी संरचनाएं हैं जो गतिशील रूप से आवंटित स्मृति में स्थित हैं। यह आमतौर पर जितना तेज़ हो सकता है आपका कोड प्राप्त कर सकता है, हाथ से अनुकूलित विधानसभा कोड भी इससे तेज नहीं हो सकता है।
एक गेटर / सेटिंग के माध्यम से एक आइवर को एक्सेस करने में एक ऑब्जेक्टिव-सी मेथड कॉल शामिल है, जो "सामान्य" सी फ़ंक्शन कॉल की तुलना में बहुत कम (कम से कम 3-4 बार) है और यहां तक कि एक सामान्य सी फ़ंक्शन कॉल पहले से कई गुना धीमी होगी। एक संरचनात्मक सदस्य तक पहुँचना। आपकी संपत्ति की विशेषताओं के आधार पर, संकलक द्वारा उत्पन्न सेटर / गेट्टर कार्यान्वयन में फ़ंक्शन objc_getProperty
/ के लिए एक और C फ़ंक्शन कॉल शामिल हो सकता है objc_setProperty
, क्योंकि इनमें आवश्यक वस्तुओं के रूप में retain
/ copy
/ autorelease
और आगे परमाणु गुणों के लिए स्पिनलॉकिंग करना आवश्यक होगा। यह आसानी से बहुत महंगा हो सकता है और मैं 50% धीमा होने की बात नहीं कर रहा हूं।
चलो यह करके देखें:
CFAbsoluteTime cft;
unsigned const kRuns = 1000 * 1000 * 1000;
cft = CFAbsoluteTimeGetCurrent();
for (unsigned i = 0; i < kRuns; i++) {
testIVar = i;
}
cft = CFAbsoluteTimeGetCurrent() - cft;
NSLog(@"1: %.1f picoseconds/run", (cft * 10000000000.0) / kRuns);
cft = CFAbsoluteTimeGetCurrent();
for (unsigned i = 0; i < kRuns; i++) {
[self setTestIVar:i];
}
cft = CFAbsoluteTimeGetCurrent() - cft;
NSLog(@"2: %.1f picoseconds/run", (cft * 10000000000.0) / kRuns);
आउटपुट:
1: 23.0 picoseconds/run
2: 98.4 picoseconds/run
यह 4.28 गुना धीमा है और यह एक गैर-परमाणु आदिम int था, बहुत अच्छा मामला है ; अधिकांश अन्य मामले और भी बुरे हैं (परमाणु NSString *
संपत्ति का प्रयास करें !)। तो अगर आप इस तथ्य के साथ रह सकते हैं कि प्रत्येक आइवर की पहुंच 4-5 गुना धीमी हो सकती है, तो गुणों का उपयोग करना ठीक है (कम से कम जब यह प्रदर्शन की बात आती है), हालांकि, ऐसी बहुत सी परिस्थितियां हैं जहां इस तरह का प्रदर्शन ड्रॉप है पूरी तरह से अस्वीकार्य।
अपडेट 2015-10-20
कुछ लोग तर्क देते हैं, कि यह एक वास्तविक दुनिया की समस्या नहीं है, ऊपर दिया गया कोड पूरी तरह से सिंथेटिक है और आप इसे कभी भी वास्तविक अनुप्रयोग में नहीं देखेंगे। ठीक है, तो चलिए असली दुनिया का नमूना आजमाते हैं।
नीचे दिए गए कोड Account
वस्तुओं को परिभाषित करता है। एक खाते में ऐसे गुण होते हैं जो अपने मालिक के नाम ( NSString *
), लिंग ( enum
) और उम्र ( unsigned
) के साथ-साथ एक संतुलन ( int64_t
) का वर्णन करते हैं । एक खाता ऑब्जेक्ट में एक init
विधि और एक compare:
विधि है। compare:
के रूप में परिभाषित किया गया है विधि: महिला आदेश पुरुष से पहले, नाम वर्णानुक्रम आदेश, वर्ष, संतुलन आदेश निम्न से उच्च से पहले युवा आदेश।
वास्तव में दो खाता कक्षाएं मौजूद हैं, AccountA
और AccountB
। यदि आप उनके कार्यान्वयन को देखते हैं, तो आप देखेंगे कि वे लगभग पूरी तरह समान हैं, एक अपवाद के साथ: compare:
विधि। AccountA
वस्तुएं अपने स्वयं के गुणों को विधि (गटर) द्वारा एक्सेस करती हैं , जबकि AccountB
ऑब्जेक्ट अपने गुणों को आइवर द्वारा एक्सेस करते हैं । यही वास्तव में एकमात्र अंतर है! वे दोनों दूसरे ऑब्जेक्ट के गुणों को गेट्टर से तुलना करने के लिए एक्सेस करते हैं (इसे आइवर द्वारा एक्सेस करना सुरक्षित नहीं होगा! क्या होगा यदि अन्य ऑब्जेक्ट एक उपवर्ग है और गेट्टर को ओवरराइड कर दिया है?)। यह भी ध्यान दें कि ivars के रूप में अपने स्वयं के गुणों तक पहुँचने से एनकैप्सुलेशन (ivars अभी भी सार्वजनिक नहीं हैं) नहीं टूटता है ।
परीक्षण सेटअप वास्तव में सरल है: 1 Mio यादृच्छिक खाते बनाएं, उन्हें एक सरणी में जोड़ें और उस सरणी को सॉर्ट करें। बस। बेशक, दो सरणियाँ हैं, एक AccountA
वस्तुओं के लिए और एक AccountB
वस्तुओं के लिए और दोनों सरणियाँ समान खातों (समान डेटा स्रोत) से भरी हुई हैं। हम समय को एरेज़ को सॉर्ट करने में कितना समय लेते हैं।
यहां मैंने कल किए गए कई रनों का उत्पादन किया है:
runTime 1: 4.827070, 5.002070, 5.014527, 5.019014, 5.123039
runTime 2: 3.835088, 3.804666, 3.792654, 3.796857, 3.871076
जैसा कि आप देख सकते हैं, AccountB
वस्तुओं के सरणी को क्रमबद्ध करने की तुलना में वस्तुओं की सरणी को क्रमबद्ध करना हमेशा महत्वपूर्ण होता है AccountA
।
जो कोई भी दावा करता है कि 1.32 सेकंड तक के रनटाइम अंतर से कोई फर्क नहीं पड़ता कि यूआई प्रोग्रामिंग को कभी भी बेहतर नहीं करना चाहिए। यदि मैं एक बड़ी तालिका के क्रमबद्ध क्रम को बदलना चाहता हूं, उदाहरण के लिए, समय की तरह ये अंतर उपयोगकर्ता के लिए एक बड़ा अंतर है (स्वीकार्य और सुस्त यूआई के बीच का अंतर)।
इसके अलावा इस मामले में नमूना कोड यहां प्रदर्शन किया जाने वाला एकमात्र वास्तविक काम है, लेकिन कितनी बार आपका कोड एक जटिल घड़ी की कल की एक छोटी सी गियर है? और अगर हर गियर इस तरह पूरी प्रक्रिया को धीमा कर देता है, तो अंत में पूरी घड़ी की गति के लिए इसका क्या मतलब है? खासकर यदि एक काम का चरण दूसरे के आउटपुट पर निर्भर करता है, जिसका अर्थ है कि सभी अक्षमताएं योग करेंगी। अधिकांश अक्षमताएं अपने आप में कोई समस्या नहीं हैं, यह उनकी पूरी राशि है जो पूरी प्रक्रिया के लिए एक समस्या बन जाती है। और इस तरह की समस्या कुछ भी नहीं है एक प्रोफाइलर आसानी से दिखाएगा क्योंकि एक प्रोफाइलर महत्वपूर्ण गर्म स्थानों को खोजने के बारे में है, लेकिन इनमें से कोई भी अक्षमता अपने आप में गर्म स्थान नहीं है। सीपीयू समय उनके बीच केवल औसत रूप से फैला हुआ है, फिर भी उनमें से प्रत्येक का केवल इतना छोटा अंश है, इसे अनुकूलित करने के लिए समय की कुल बर्बादी लगती है। और यह सच है,
और यहां तक कि अगर आप सीपीयू समय के संदर्भ में नहीं सोचते हैं, क्योंकि आपको लगता है कि सीपीयू समय बर्बाद करना पूरी तरह से स्वीकार्य है, आखिरकार "यह मुफ़्त है", तो बिजली की खपत के कारण सर्वर होस्टिंग लागत के बारे में क्या? मोबाइल उपकरणों की बैटरी रनटाइम के बारे में क्या? यदि आप एक ही मोबाइल ऐप को दो बार लिखते हैं (उदाहरण के लिए एक मोबाइल वेब ब्राउज़र), तो एक बार एक ऐसा संस्करण जहां सभी वर्ग अपने गुणों को केवल गेटर्स द्वारा एक्सेस करते हैं और एक बार जहां सभी वर्ग केवल आइवर द्वारा उन्हें एक्सेस करते हैं, पहले एक लगातार उपयोग करके निश्चित रूप से नाली होगी। बैटरी एक दूसरे का उपयोग करने की तुलना में बहुत तेज है, भले ही वे कार्यात्मक समतुल्य हैं और उपयोगकर्ता के लिए दूसरा एक भी शायद थोड़ा स्वैटर महसूस होगा।
अब यहाँ आपकी main.m
फाइल के लिए कोड (ARC सक्षम होने पर कोड निर्भर करता है और पूर्ण प्रभाव देखने के लिए संकलन करते समय अनुकूलन का उपयोग करना सुनिश्चित करें):
#import <Foundation/Foundation.h>
typedef NS_ENUM(int, Gender) {
GenderMale,
GenderFemale
};
@interface AccountA : NSObject
@property (nonatomic) unsigned age;
@property (nonatomic) Gender gender;
@property (nonatomic) int64_t balance;
@property (nonatomic,nonnull,copy) NSString * name;
- (NSComparisonResult)compare:(nonnull AccountA *const)account;
- (nonnull instancetype)initWithName:(nonnull NSString *const)name
age:(const unsigned)age gender:(const Gender)gender
balance:(const int64_t)balance;
@end
@interface AccountB : NSObject
@property (nonatomic) unsigned age;
@property (nonatomic) Gender gender;
@property (nonatomic) int64_t balance;
@property (nonatomic,nonnull,copy) NSString * name;
- (NSComparisonResult)compare:(nonnull AccountB *const)account;
- (nonnull instancetype)initWithName:(nonnull NSString *const)name
age:(const unsigned)age gender:(const Gender)gender
balance:(const int64_t)balance;
@end
static
NSMutableArray * allAcocuntsA;
static
NSMutableArray * allAccountsB;
static
int64_t getRandom ( const uint64_t min, const uint64_t max ) {
assert(min <= max);
uint64_t rnd = arc4random(); // arc4random() returns a 32 bit value only
rnd = (rnd << 32) | arc4random();
rnd = rnd % ((max + 1) - min); // Trim it to range
return (rnd + min); // Lift it up to min value
}
static
void createAccounts ( const NSUInteger ammount ) {
NSArray *const maleNames = @[
@"Noah", @"Liam", @"Mason", @"Jacob", @"William",
@"Ethan", @"Michael", @"Alexander", @"James", @"Daniel"
];
NSArray *const femaleNames = @[
@"Emma", @"Olivia", @"Sophia", @"Isabella", @"Ava",
@"Mia", @"Emily", @"Abigail", @"Madison", @"Charlotte"
];
const NSUInteger nameCount = maleNames.count;
assert(maleNames.count == femaleNames.count); // Better be safe than sorry
allAcocuntsA = [NSMutableArray arrayWithCapacity:ammount];
allAccountsB = [NSMutableArray arrayWithCapacity:ammount];
for (uint64_t i = 0; i < ammount; i++) {
const Gender g = (getRandom(0, 1) == 0 ? GenderMale : GenderFemale);
const unsigned age = (unsigned)getRandom(18, 120);
const int64_t balance = (int64_t)getRandom(0, 200000000) - 100000000;
NSArray *const nameArray = (g == GenderMale ? maleNames : femaleNames);
const NSUInteger nameIndex = (NSUInteger)getRandom(0, nameCount - 1);
NSString *const name = nameArray[nameIndex];
AccountA *const accountA = [[AccountA alloc]
initWithName:name age:age gender:g balance:balance
];
AccountB *const accountB = [[AccountB alloc]
initWithName:name age:age gender:g balance:balance
];
[allAcocuntsA addObject:accountA];
[allAccountsB addObject:accountB];
}
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
@autoreleasepool {
NSUInteger ammount = 1000000; // 1 Million;
if (argc > 1) {
unsigned long long temp = 0;
if (1 == sscanf(argv[1], "%llu", &temp)) {
// NSUIntegerMax may just be UINT32_MAX!
ammount = (NSUInteger)MIN(temp, NSUIntegerMax);
}
}
createAccounts(ammount);
}
// Sort A and take time
const CFAbsoluteTime startTime1 = CFAbsoluteTimeGetCurrent();
@autoreleasepool {
[allAcocuntsA sortedArrayUsingSelector:@selector(compare:)];
}
const CFAbsoluteTime runTime1 = CFAbsoluteTimeGetCurrent() - startTime1;
// Sort B and take time
const CFAbsoluteTime startTime2 = CFAbsoluteTimeGetCurrent();
@autoreleasepool {
[allAccountsB sortedArrayUsingSelector:@selector(compare:)];
}
const CFAbsoluteTime runTime2 = CFAbsoluteTimeGetCurrent() - startTime2;
NSLog(@"runTime 1: %f", runTime1);
NSLog(@"runTime 2: %f", runTime2);
}
return 0;
}
@implementation AccountA
- (NSComparisonResult)compare:(nonnull AccountA *const)account {
// Sort by gender first! Females prior to males.
if (self.gender != account.gender) {
if (self.gender == GenderFemale) return NSOrderedAscending;
return NSOrderedDescending;
}
// Otherwise sort by name
if (![self.name isEqualToString:account.name]) {
return [self.name compare:account.name];
}
// Otherwise sort by age, young to old
if (self.age != account.age) {
if (self.age < account.age) return NSOrderedAscending;
return NSOrderedDescending;
}
// Last ressort, sort by balance, low to high
if (self.balance != account.balance) {
if (self.balance < account.balance) return NSOrderedAscending;
return NSOrderedDescending;
}
// If we get here, the are really equal!
return NSOrderedSame;
}
- (nonnull instancetype)initWithName:(nonnull NSString *const)name
age:(const unsigned)age gender:(const Gender)gender
balance:(const int64_t)balance
{
self = [super init];
assert(self); // We promissed to never return nil!
_age = age;
_gender = gender;
_balance = balance;
_name = [name copy];
return self;
}
@end
@implementation AccountB
- (NSComparisonResult)compare:(nonnull AccountA *const)account {
// Sort by gender first! Females prior to males.
if (_gender != account.gender) {
if (_gender == GenderFemale) return NSOrderedAscending;
return NSOrderedDescending;
}
// Otherwise sort by name
if (![_name isEqualToString:account.name]) {
return [_name compare:account.name];
}
// Otherwise sort by age, young to old
if (_age != account.age) {
if (_age < account.age) return NSOrderedAscending;
return NSOrderedDescending;
}
// Last ressort, sort by balance, low to high
if (_balance != account.balance) {
if (_balance < account.balance) return NSOrderedAscending;
return NSOrderedDescending;
}
// If we get here, the are really equal!
return NSOrderedSame;
}
- (nonnull instancetype)initWithName:(nonnull NSString *const)name
age:(const unsigned)age gender:(const Gender)gender
balance:(const int64_t)balance
{
self = [super init];
assert(self); // We promissed to never return nil!
_age = age;
_gender = gender;
_balance = balance;
_name = [name copy];
return self;
}
@end