আমার জন্য এটি সাধারণত পারফরম্যান্স হয়। একটি অবজেক্টের আইভার অ্যাক্সেস করা যেমন স্ট্রাকযুক্ত মেমোরিতে পয়েন্টার ব্যবহার করে সিতে স্ট্রাক্ট সদস্যের অ্যাক্সেসের তত দ্রুত। আসলে, অবজেক্টিভ-সি অবজেক্টগুলি মূলত সি স্ট্রাক্টগুলি গতিশীল বরাদ্দ মেমরিতে অবস্থিত। এটি সাধারণত আপনার কোডটি পেতে পারে তত দ্রুত, হ্যান্ড অপ্টিমাইজড অ্যাসেমব্লিং কোডও এর চেয়ে দ্রুততর হতে পারে না।
গিটার / সেটিংয়ের মাধ্যমে আইভার অ্যাক্সেস করার সাথে একটি অবজেক্টিভ-সি পদ্ধতি কল কল জড়িত, যা একটি "সাধারণ" সি ফাংশন কলের চেয়ে অনেক ধীর (কমপক্ষে 3-4 বার) এবং এমনকি একটি সাধারণ সি ফাংশন কল ইতিমধ্যে একাধিকবার ধীর হতে পারে একটি কাঠামো সদস্য অ্যাক্সেস করা। আপনার সম্পত্তি গুণাবলীর উপর নির্ভর করে, সেটার / সংগ্রহকারী কম্পাইলার দ্বারা উত্পন্ন বাস্তবায়ন ফাংশন অন্য সি ফাংশন কল থাকতে পারে objc_getProperty
/ objc_setProperty
এই থাকবে হিসাবে retain
/ copy
/ autorelease
যেমন বস্তু প্রয়োজন এবং আরও পারমাণবিক বৈশিষ্ট্যের জন্য spinlocking সঞ্চালন যেখানে প্রয়োজন। এটি সহজেই খুব ব্যয়বহুল হতে পারে এবং আমি 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
এটি ৪.২৮ গুণ ধীর এবং এটি একটি অ-পারমাণবিক আদিম প্রান্ত ছিল, এটি সর্বোত্তমতম ঘটনা ; বেশিরভাগ অন্যান্য ক্ষেত্রে আরও খারাপ হয় (একটি পারমাণবিক NSString *
সম্পত্তি চেষ্টা করুন !)। সুতরাং আপনি যদি প্রতিটি আইভার অ্যাক্সেসের তুলনায় 4-5 গুণ কম ধীরে ধীরে বেঁচে থাকতে পারেন তবে বৈশিষ্ট্যগুলি ব্যবহার করা ভাল (কমপক্ষে যখন এটি কার্য সম্পাদনের ক্ষেত্রে আসে), তবে, এমন অনেকগুলি পরিস্থিতি রয়েছে যেখানে এই ধরনের পারফরম্যান্স ড্রপ হয় সম্পূর্ণ অগ্রহণযোগ্য।
আপডেট 2015-10-20
কিছু লোক যুক্তি দেখান যে এটি কোনও আসল বিশ্বের সমস্যা নয়, উপরের কোডটি নিখুঁতভাবে সিন্থেটিক এবং আপনি কখনই এটি বাস্তব প্রয়োগে লক্ষ্য করবেন না। ঠিক আছে, তাহলে আসুন একটি বাস্তব বিশ্বের নমুনা চেষ্টা করি।
নীচের কোডগুলি Account
অবজেক্টগুলি সংজ্ঞায়িত করে। কোনও অ্যাকাউন্টে এমন বৈশিষ্ট্য রয়েছে যা তার মালিকের নাম ( NSString *
), লিঙ্গ ( enum
), এবং বয়স ( unsigned
) পাশাপাশি একটি ভারসাম্য ( int64_t
) বর্ণনা করে। একটি অ্যাকাউন্ট অবজেক্টের একটি init
পদ্ধতি এবং একটি compare:
পদ্ধতি রয়েছে। compare:
পদ্ধতিটি এই হিসাবে সংজ্ঞায়িত করা হয়: পুরুষের আগে মহিলা আদেশ, নাম বর্ণানুক্রমিকভাবে অর্ডার করে, পুরানো আগে যুবক অর্ডারগুলি, ভারসাম্যগুলি কম থেকে কম।
আসলে এখানে দুটি অ্যাকাউন্ট ক্লাস রয়েছে, AccountA
এবং AccountB
। যদি আপনি তাদের বাস্তবায়নের দিকে লক্ষ্য করেন তবে লক্ষ্য করবেন যে এগুলি একটি ব্যাতিক্রম সহ প্রায় সম্পূর্ণ অভিন্ন: compare:
পদ্ধতি। AccountA
বস্তু অ্যাক্সেস তাদের নিজস্ব বৈশিষ্ট্য , পদ্ধতি (সংগ্রহকারী) দ্বারা যখন AccountB
বস্তু অ্যাক্সেস তাদের নিজস্ব বৈশিষ্ট্য ivar দ্বারা। এটাই আসলে একমাত্র তফাত! তারা উভয়ই গেটর দ্বারা তুলনা করার জন্য অন্য বস্তুর বৈশিষ্ট্যগুলিতে অ্যাক্সেস করে (আইভার দ্বারা এটি অ্যাক্সেস করা নিরাপদ হবে না! যদি অন্য অবজেক্টটি সাবক্লাস হয় এবং গিটারটিকে ওভাররাইড করে ফেলে থাকে তবে কী হবে?) এছাড়াও খেয়াল করুন যে আইভার হিসাবে আপনার নিজস্ব সম্পত্তি অ্যাক্সেস করলে এনক্যাপসুলেশনটি ভেঙে যায় না (আইভারগুলি এখনও সর্বজনীন নয়)।
পরীক্ষার সেটআপটি সত্যই সহজ: 1 টি মিয়ো এলোমেলো অ্যাকাউন্ট তৈরি করুন, তাদের একটি অ্যারেতে যুক্ত করুন এবং সেই অ্যারেটি বাছাই করুন। এটাই. অবশ্যই, দুটি অ্যারে রয়েছে, একটি 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
।
যে কেউ দাবি করে যে ১.৩৩ সেকেন্ড পর্যন্ত রানটাইম পার্থক্য কোনও পার্থক্য রাখে না তার চেয়ে ভাল ইউআই প্রোগ্রামিং কখনই করা উচিত নয়। যদি আমি একটি বড় টেবিলের বাছাইয়ের ক্রমটি পরিবর্তন করতে চাই, উদাহরণস্বরূপ, এই সময়ের মতো পার্থক্যগুলি ব্যবহারকারীর জন্য একটি বিশাল পার্থক্য করে (গ্রহণযোগ্য এবং একটি স্লথ UI মধ্যে পার্থক্য)।
এছাড়াও এক্ষেত্রে স্যাম্পল কোডটিই এখানে সম্পাদিত একমাত্র আসল কাজ, তবে আপনার কোডটি কত ঘন ঘন জটিল ক্লকওয়ার্কের কেবলমাত্র একটি ছোট গিয়ার হয়? এবং প্রতিটি গিয়ার যদি পুরো প্রক্রিয়াটিকে এভাবে ধীর করে দেয় তবে শেষেরটি পুরো ঘড়ির কাঁটার গতির জন্য কী বোঝায়? বিশেষত যদি একটি কাজের পদক্ষেপ অন্য একের আউটপুটের উপর নির্ভর করে, যার অর্থ সমস্ত অদক্ষতা যোগ হবে। বেশিরভাগ অদক্ষতা তাদের নিজেরাই সমস্যা নয়, এটি তাদের নিখুঁত যোগফল যা পুরো প্রক্রিয়াতে সমস্যা হয়ে দাঁড়ায়। এবং এই জাতীয় সমস্যা হ'ল কোনও প্রোফাইলার সহজেই দেখাতে পারবেন না কারণ কোনও প্রোফাইলার সমালোচনামূলক গরম দাগগুলি সন্ধান করতে চলেছেন, তবে এই অদক্ষতার কোনওটিই তাদের নিজেরাই গরম দাগ নয়। সিপিইউ সময়টি তাদের মধ্যে গড়ে গড়ে ছড়িয়ে যায়, তবুও তাদের প্রত্যেকেরই এর মধ্যে এর মতো একটি ক্ষুদ্র ভগ্নাংশ থাকে, এটি অনুকূলিতকরণের জন্য মোট সময় অপচয় বলে মনে হয়। এবং এটা সত্য,
এমনকি আপনি যদি সিপিইউ সময়ের বিবেচনা নাও করেন, কারণ আপনি বিশ্বাস করেন যে সিপিইউ সময় নষ্ট করা পুরোপুরি গ্রহণযোগ্য, সর্বোপরি "এটি নিখরচায়", তারপরে বিদ্যুৎ খরচজনিত সার্ভার হোস্টিংয়ের খরচ কী? মোবাইল ডিভাইসগুলির ব্যাটারি রানটাইম সম্পর্কে কী? আপনি যদি একই মোবাইল অ্যাপ্লিকেশনটি দু'বার লিখেন (যেমন একটি নিজস্ব মোবাইল ওয়েব ব্রাউজার), একবার এমন সংস্করণ যেখানে সমস্ত শ্রেণি কেবলমাত্র গেটর দ্বারা তাদের নিজস্ব সম্পত্তি অ্যাক্সেস করে এবং একবার যেখানে সমস্ত শ্রেণি কেবলমাত্র আইভারদের মাধ্যমে এগুলি অ্যাক্সেস করে, প্রথমটি অবিচ্ছিন্নভাবে ব্যবহার করে তা নিষ্ক্রিয় হয়ে যাবে ব্যাটারি দ্বিতীয়টি ব্যবহারের চেয়ে অনেক দ্রুত, যদিও এটি কার্যকরী সমতুল্য এবং ব্যবহারকারীর কাছে দ্বিতীয়টি সম্ভবত কিছুটা সুইফারও বোধ করে।
এখন আপনার main.m
ফাইলে কোডটি এখানে রয়েছে (কোডটি এআরসি সক্ষম হওয়ার উপর নির্ভর করে এবং পুরো প্রভাবটি দেখার জন্য সংকলন করার সময় অপ্টিমাইজেশন ব্যবহার করতে ভুলবেন না):
#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