มีวิธีง่ายๆในการหาเวลาอย่างแม่นยำหรือไม่?
ฉันต้องการคำนวณความล่าช้าระหว่างการเรียกใช้เมธอด โดยเฉพาะอย่างยิ่งฉันต้องการคำนวณความเร็วในการเลื่อนใน UIScrollView
มีวิธีง่ายๆในการหาเวลาอย่างแม่นยำหรือไม่?
ฉันต้องการคำนวณความล่าช้าระหว่างการเรียกใช้เมธอด โดยเฉพาะอย่างยิ่งฉันต้องการคำนวณความเร็วในการเลื่อนใน UIScrollView
คำตอบ:
NSDate
และtimeIntervalSince*
วิธีการจะส่งกลับNSTimeInterval
ซึ่งเป็นสองเท่าที่มีความแม่นยำย่อยมิลลิวินาที NSTimeInterval
เป็นหน่วยวินาที แต่จะใช้การเพิ่มสองครั้งเพื่อให้คุณมีความแม่นยำ
ในการคำนวณความแม่นยำของเวลาในระดับมิลลิวินาทีคุณสามารถทำได้:
// Get a current time for where you want to start measuring from
NSDate *date = [NSDate date];
// do work...
// Find elapsed time and convert to milliseconds
// Use (-) modifier to conversion since receiver is earlier than now
double timePassed_ms = [date timeIntervalSinceNow] * -1000.0;
เอกสารตรงเวลา
มีวิธีการอื่น ๆ อีกมากมายในการคำนวณช่วงเวลานี้โดยใช้เป็นNSDate
ฉันจะแนะนำให้ดูที่เอกสารประกอบการเรียนสำหรับและNSDate
ซึ่งพบได้ในNSDate ชั้นอ้างอิง
NSDate
และmach_absolute_time()
อยู่ในระดับประมาณ 30 มิลลิวินาที 27 เทียบกับ 29, 36 เทียบกับ 39, 43 เทียบกับ 45 ใช้NSDate
ง่ายกว่าสำหรับฉันและผลลัพธ์ก็คล้ายกันมากพอที่จะไม่สนใจ
mach_absolute_time()
สามารถใช้เพื่อรับการวัดที่แม่นยำ
โปรดดูhttp://developer.apple.com/qa/qa2004/qa1398.html
นอกจากนี้ยังมีCACurrentMediaTime()
ซึ่งโดยพื้นฐานแล้วจะเหมือนกัน แต่มีอินเทอร์เฟซที่ใช้งานง่ายกว่า
(หมายเหตุ: คำตอบนี้เขียนขึ้นในปี 2009 ดูคำตอบของ Pavel Alexeev สำหรับclock_gettime()
อินเทอร์เฟซPOSIX ที่ง่ายกว่าซึ่งมีอยู่ใน macOS และ iOS เวอร์ชันใหม่กว่า)
CACurrentMediaTime()
ซึ่งจะแปลงmach_absolute_time()
เป็นไฟล์double
.
elapsedNano
เป็นประเภทNanoseconds
ซึ่งไม่ใช่ประเภทจำนวนเต็มธรรมดา เป็นนามแฝงUnsignedWide
ซึ่งเป็นโครงสร้างที่มีช่องจำนวนเต็ม 32 บิตสองช่อง คุณสามารถใช้UnsignedWideToUInt64()
แทนนักแสดงได้หากต้องการ
กรุณาอย่าใช้NSDate
, CFAbsoluteTimeGetCurrent
หรือgettimeofday
การวัดเวลาที่ผ่านไป ทั้งหมดนี้ขึ้นอยู่กับนาฬิกาของระบบซึ่งสามารถเปลี่ยนแปลงได้ตลอดเวลาเนื่องจากสาเหตุหลายประการเช่นการซิงค์เวลาเครือข่าย (NTP) การอัปเดตนาฬิกา (มักเกิดขึ้นเพื่อปรับเวลาดริฟท์) การปรับ DST วินาทีอธิกสุรทินและอื่น ๆ
ซึ่งหมายความว่าหากคุณวัดความเร็วในการดาวน์โหลดหรืออัปโหลดคุณจะได้รับตัวเลขที่เพิ่มขึ้นอย่างรวดเร็วหรือลดลงอย่างกะทันหันซึ่งไม่สัมพันธ์กับสิ่งที่เกิดขึ้น การทดสอบประสิทธิภาพของคุณจะมีค่าผิดปกติแปลก ๆ และตัวจับเวลาด้วยตนเองของคุณจะทำงานหลังจากระยะเวลาที่ไม่ถูกต้อง เวลาอาจย้อนกลับไปและคุณจะจบลงด้วยเดลต้าเชิงลบและคุณสามารถจบลงด้วยการเรียกซ้ำไม่สิ้นสุดหรือรหัสตาย (ใช่ฉันได้ทำทั้งสองอย่างนี้แล้ว)
ใช้mach_absolute_time
. จะวัดวินาทีจริงตั้งแต่เริ่มต้นเคอร์เนล มันเพิ่มขึ้นอย่างจำเจ (จะไม่ถอยหลัง) และไม่ได้รับผลกระทบจากการตั้งค่าวันที่และเวลา เนื่องจากมันเป็นเรื่องยากที่จะทำงานด้วยนี่คือกระดาษห่อตัวง่ายๆที่ให้คุณNSTimeInterval
:
// LBClock.h
@interface LBClock : NSObject
+ (instancetype)sharedClock;
// since device boot or something. Monotonically increasing, unaffected by date and time settings
- (NSTimeInterval)absoluteTime;
- (NSTimeInterval)machAbsoluteToTimeInterval:(uint64_t)machAbsolute;
@end
// LBClock.m
#include <mach/mach.h>
#include <mach/mach_time.h>
@implementation LBClock
{
mach_timebase_info_data_t _clock_timebase;
}
+ (instancetype)sharedClock
{
static LBClock *g;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
g = [LBClock new];
});
return g;
}
- (id)init
{
if(!(self = [super init]))
return nil;
mach_timebase_info(&_clock_timebase);
return self;
}
- (NSTimeInterval)machAbsoluteToTimeInterval:(uint64_t)machAbsolute
{
uint64_t nanos = (machAbsolute * _clock_timebase.numer) / _clock_timebase.denom;
return nanos/1.0e9;
}
- (NSTimeInterval)absoluteTime
{
uint64_t machtime = mach_absolute_time();
return [self machAbsoluteToTimeInterval:machtime];
}
@end
CACurrentMediaTime()
จาก QuartzCore ล่ะ?
@import Darwin;
แทน#include <mach/mach_time.h>
CFAbsoluteTimeGetCurrent()
คืนค่าเวลาสัมบูรณ์เป็นdouble
ค่า แต่ฉันไม่รู้ว่าความแม่นยำคืออะไร - อาจอัปเดตทุก ๆ สิบมิลลิวินาทีเท่านั้นหรืออาจอัปเดตทุก ๆ ไมโครวินาทีฉันไม่รู้
72.89674947369
วินาทีจะค่อนข้างผิดปกติเมื่อพิจารณาจากค่าอื่น ๆ ทั้งหมดที่อาจเป็นได้ ;)
ฉันจะไม่ใช้mach_absolute_time()
เพราะมันค้นหาการรวมกันของเคอร์เนลและโปรเซสเซอร์เป็นเวลาที่แน่นอนโดยใช้เห็บ (อาจเป็นเวลาทำงาน)
สิ่งที่ฉันจะใช้:
CFAbsoluteTimeGetCurrent();
ฟังก์ชันนี้ได้รับการปรับให้เหมาะสมเพื่อแก้ไขความแตกต่างของซอฟต์แวร์และฮาร์ดแวร์ iOS และ OSX
บางสิ่ง Geekier
ผลหารของความแตกต่างในmach_absolute_time()
และAFAbsoluteTimeGetCurrent()
เป็นประมาณ 24000011.154871 เสมอ
นี่คือบันทึกแอปของฉัน:
โปรดทราบว่าเวลาผลลัพธ์สุดท้ายเป็นความแตกต่างCFAbsoluteTimeGetCurrent()
ของ
2012-03-19 21:46:35.609 Rest Counter[3776:707] First Time: 353900795.609040
2012-03-19 21:46:36.360 Rest Counter[3776:707] Second Time: 353900796.360177
2012-03-19 21:46:36.361 Rest Counter[3776:707] Final Result Time (difference): 0.751137
2012-03-19 21:46:36.363 Rest Counter[3776:707] Mach absolute time: 18027372
2012-03-19 21:46:36.365 Rest Counter[3776:707] Mach absolute time/final time: 24000113.153295
2012-03-19 21:46:36.367 Rest Counter[3776:707] -----------------------------------------------------
2012-03-19 21:46:43.074 Rest Counter[3776:707] First Time: 353900803.074637
2012-03-19 21:46:43.170 Rest Counter[3776:707] Second Time: 353900803.170256
2012-03-19 21:46:43.172 Rest Counter[3776:707] Final Result Time (difference): 0.095619
2012-03-19 21:46:43.173 Rest Counter[3776:707] Mach absolute time: 2294833
2012-03-19 21:46:43.175 Rest Counter[3776:707] Mach absolute time/final time: 23999753.727777
2012-03-19 21:46:43.177 Rest Counter[3776:707] -----------------------------------------------------
2012-03-19 21:46:46.499 Rest Counter[3776:707] First Time: 353900806.499199
2012-03-19 21:46:55.017 Rest Counter[3776:707] Second Time: 353900815.016985
2012-03-19 21:46:55.018 Rest Counter[3776:707] Final Result Time (difference): 8.517786
2012-03-19 21:46:55.020 Rest Counter[3776:707] Mach absolute time: 204426836
2012-03-19 21:46:55.022 Rest Counter[3776:707] Mach absolute time/final time: 23999996.639500
2012-03-19 21:46:55.024 Rest Counter[3776:707] -----------------------------------------------------
mach_absolute_time()
ด้วยและแล้วก็mach_timebase_info_data
(long double)((mach_absolute_time()*time_base.numer)/((1000*1000)*time_base.denom));
หากต้องการรับฐานเวลาของเครื่องจักรคุณสามารถทำได้(void)mach_timebase_info(&your_timebase);
#define CTTimeStart() NSDate * __date = [NSDate date]
#define CTTimeEnd(MSG) NSLog(MSG " %g",[__date timeIntervalSinceNow]*-1)
การใช้งาน:
CTTimeStart();
...
CTTimeEnd(@"that was a long time:");
เอาท์พุต:
2013-08-23 15:34:39.558 App-Dev[21229:907] that was a long time: .0023
NSDate
ขึ้นอยู่กับนาฬิกาของระบบซึ่งสามารถเปลี่ยนแปลงได้ตลอดเวลาซึ่งอาจส่งผลให้ช่วงเวลาผิดพลาดหรือแม้แต่ติดลบ ใช้mach_absolute_time
แทนเพื่อดูเวลาที่ผ่านไปอย่างถูกต้อง
mach_absolute_time
อาจได้รับผลกระทบจากการรีบูตอุปกรณ์ ใช้เวลาเซิร์ฟเวอร์แทน
นอกจากนี้วิธีการคำนวณ 64 บิตNSNumber
เริ่มต้นด้วย Unix epoch ในหน่วยมิลลิวินาทีในกรณีที่คุณต้องการจัดเก็บใน CoreData ฉันต้องการสิ่งนี้สำหรับแอปของฉันที่โต้ตอบกับระบบที่จัดเก็บวันที่ด้วยวิธีนี้
+ (NSNumber*) longUnixEpoch {
return [NSNumber numberWithLongLong:[[NSDate date] timeIntervalSince1970] * 1000];
}
ฟังก์ชั่นที่ใช้mach_absolute_time
เหมาะสำหรับการวัดระยะสั้น
แต่สำหรับการวัดระยะยาวข้อแม้ที่สำคัญก็คือพวกเขาจะหยุดติ๊กในขณะที่อุปกรณ์อยู่ในโหมดสลีป
มีฟังก์ชั่นรับเวลาตั้งแต่บูต มันไม่หยุดในขณะนอนหลับ นอกจากนี้gettimeofday
ไม่ได้เป็นเสียงเดียว แต่ในการทดลองของฉันฉันมักจะเห็นว่าเวลาในการบูตจะเปลี่ยนไปเมื่อเวลาของระบบเปลี่ยนไปดังนั้นฉันคิดว่ามันน่าจะใช้ได้ดี
func timeSinceBoot() -> TimeInterval
{
var bootTime = timeval()
var currentTime = timeval()
var timeZone = timezone()
let mib = UnsafeMutablePointer<Int32>.allocate(capacity: 2)
mib[0] = CTL_KERN
mib[1] = KERN_BOOTTIME
var size = MemoryLayout.size(ofValue: bootTime)
var timeSinceBoot = 0.0
gettimeofday(¤tTime, &timeZone)
if sysctl(mib, 2, &bootTime, &size, nil, 0) != -1 && bootTime.tv_sec != 0 {
timeSinceBoot = Double(currentTime.tv_sec - bootTime.tv_sec)
timeSinceBoot += Double(currentTime.tv_usec - bootTime.tv_usec) / 1000000.0
}
return timeSinceBoot
}
และตั้งแต่ iOS 10 และ macOS 10.12 เราสามารถใช้ CLOCK_MONOTONIC:
if #available(OSX 10.12, *) {
var uptime = timespec()
if clock_gettime(CLOCK_MONOTONIC_RAW, &uptime) == 0 {
return Double(uptime.tv_sec) + Double(uptime.tv_nsec) / 1000000000.0
}
}
สรุปได้:
Date.timeIntervalSinceReferenceDate
- เปลี่ยนแปลงเมื่อเวลาของระบบเปลี่ยนไปไม่ใช่เสียงเดียวCFAbsoluteTimeGetCurrent()
- ไม่ใช่เสียงเดียวอาจย้อนกลับได้CACurrentMediaTime()
- หยุดติ๊กเมื่ออุปกรณ์อยู่ในโหมดสลีปtimeSinceBoot()
- ไม่ได้นอนหลับ แต่อาจไม่ใช่เสียงเดียวCLOCK_MONOTONIC
- ไม่นอนไม่หลับเสียงเดียวรองรับตั้งแต่ iOS 10ฉันรู้ว่านี่เป็นเรื่องเก่า แต่ฉันก็พบว่าตัวเองหลงทางอีกครั้งดังนั้นฉันคิดว่าฉันจะส่งตัวเลือกของตัวเองมาที่นี่
ทางออกที่ดีที่สุดคือตรวจสอบโพสต์บล็อกของฉันเกี่ยวกับสิ่งนี้: จับเวลาใน Objective-C: นาฬิกาจับเวลา
โดยพื้นฐานแล้วฉันเขียนคลาสที่หยุดดูด้วยวิธีพื้นฐาน แต่ถูกห่อหุ้มไว้เพื่อให้คุณต้องทำสิ่งต่อไปนี้เท่านั้น
[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];
และคุณจะจบลงด้วย:
MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029]
ในบันทึก ...
ตรวจสอบโพสต์ของฉันอีกครั้งหรือดาวน์โหลดได้ที่นี่: MMStopwatch.zip
NSDate
ขึ้นอยู่กับนาฬิกาของระบบซึ่งสามารถเปลี่ยนแปลงได้ตลอดเวลาซึ่งอาจส่งผลให้ช่วงเวลาผิดพลาดหรือแม้แต่ติดลบ ใช้mach_absolute_time
แทนเพื่อดูเวลาที่ผ่านไปอย่างถูกต้อง
คุณสามารถรับเวลาปัจจุบันเป็นมิลลิวินาทีตั้งแต่วันที่ 1 มกราคม 1970 โดยใช้ NSDate:
- (double)currentTimeInMilliseconds {
NSDate *date = [NSDate date];
return [date timeIntervalSince1970]*1000;
}
สำหรับผู้ที่เราต้องการคำตอบของ @Jeff Thompson เวอร์ชัน Swift:
// Get a current time for where you want to start measuring from
var date = NSDate()
// do work...
// Find elapsed time and convert to milliseconds
// Use (-) modifier to conversion since receiver is earlier than now
var timePassed_ms: Double = date.timeIntervalSinceNow * -1000.0
ฉันหวังว่านี่จะช่วยคุณได้
NSDate
ขึ้นอยู่กับนาฬิกาของระบบซึ่งสามารถเปลี่ยนแปลงได้ตลอดเวลาซึ่งอาจส่งผลให้ช่วงเวลาผิดพลาดหรือแม้แต่ติดลบ ใช้mach_absolute_time
แทนเพื่อดูเวลาที่ผ่านไปอย่างถูกต้อง