44//  found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
55
66#import  < CoreML/CoreML.h> 
7+ #include  < mach/mach_time.h> 
78#import  " CoreMLPythonArray.h" 
89#import  " CoreMLPython.h" 
910#import  " CoreMLPythonUtils.h" 
289290    }
290291#endif 
291292
293+ uint64_t  convertMachTimeToNanoSeconds (uint64_t  time) {
294+     static  dispatch_once_t  once;
295+     static  mach_timebase_info_data_t  timebase;
296+     dispatch_once (&once, ^{
297+         mach_timebase_info (&timebase);
298+     });
299+     uint64_t  result = (time * timebase.numer ) / timebase.denom ;
300+     return  result;
301+ }
292302
293303#if  ML_MODEL_ASSET_IS_AVAILABLE
294304    API_AVAILABLE (macos(13.0 ))
295-     MLModel * createModelFromModelAsset(MLModelAsset *modelAsset,
296-                                         MLModelConfiguration *configuration,
297-                                         NSError  * __autoreleasing *error) {
305+     std::pair<MLModel *, uint64_t> createModelFromModelAsset(
306+         MLModelAsset *modelAsset,
307+         MLModelConfiguration *configuration,
308+         NSError  * __autoreleasing *error
309+     ) {
298310        dispatch_semaphore_t  sem = dispatch_semaphore_create (0 );
299311        __block MLModel *result = nil ;
300312        __block NSError  *lError = nil ;
313+         uint64_t  loadStartTime = mach_absolute_time ();
314+         __block uint64_t  loadEndTime = loadStartTime;
301315        [MLModel loadModelAsset: modelAsset
302316                 configuration: configuration
303317             completionHandler: ^(MLModel * _Nullable model, NSError  * _Nullable loadError){
304318                result = model;
305319                lError = loadError;
320+                 loadEndTime = mach_absolute_time ();
306321                dispatch_semaphore_signal (sem);
307322        }];
308323
312327            *error = lError;
313328        }
314329
315-         return  result;
330+         uint64_t  loadDurationInNanoSeconds = convertMachTimeToNanoSeconds (loadEndTime - loadStartTime);
331+         return  {result, loadDurationInNanoSeconds};
316332    }
317- 
318333#endif 
319334}
320335
@@ -380,18 +395,25 @@ bool usingMacOS13OrHigher() {
380395            configuration.functionName  = [NSString  stringWithUTF8String: functionName.c_str ()];
381396        }
382397#endif 
398+         uint64_t  loadDurationInNanoSeconds = 0 ;
383399        //  Create MLModel
384400        if  (asset.is_none ()) {
401+             uint64_t  loadStartTime = mach_absolute_time ();
385402            m_model = [MLModel modelWithContentsOfURL: compiledUrl configuration: configuration error: &error];
403+             uint64_t  loadEndTime = mach_absolute_time ();
404+             loadDurationInNanoSeconds = convertMachTimeToNanoSeconds (loadEndTime - loadStartTime);
386405        } else  {
387406#if  ML_MODEL_ASSET_IS_AVAILABLE
388-             m_model = createModelFromModelAsset (py::cast<ModelAsset>(asset).getImpl (), configuration, &error);
407+             auto  pair = createModelFromModelAsset (py::cast<ModelAsset>(asset).getImpl (), configuration, &error);
408+             m_model = pair.first ;
409+             loadDurationInNanoSeconds = pair.second ;
389410#else 
390411            throw  std::runtime_error (" MLModelAsset is only available on macOS >= 13.0"  );
391412#endif 
392413        }
393414
394415        Utils::handleError (error);
416+         m_loadDurationInNanoSeconds = loadDurationInNanoSeconds;
395417    }
396418}
397419
@@ -410,13 +432,14 @@ bool usingMacOS13OrHigher() {
410432}
411433
412434
413- py::dict Model::predict (const  py::dict& input, State* state) const   {
435+ py::dict Model::predict (const  py::dict& input, State* state) {
414436    @autoreleasepool {
415437        NSError  *error = nil ;
416438        MLDictionaryFeatureProvider *inFeatures = Utils::dictToFeatures (input, &error);
417439        Utils::handleError (error);
418440
419441        id <MLFeatureProvider> outFeatures;
442+         uint64_t  predictStartTime = mach_absolute_time ();
420443#if  BUILT_WITH_MACOS15_SDK
421444        if  (state == NULL ) {
422445          outFeatures = [m_model predictionFromFeatures: static_cast <MLDictionaryFeatureProvider * _Nonnull>(inFeatures)
@@ -430,8 +453,10 @@ bool usingMacOS13OrHigher() {
430453        outFeatures = [m_model predictionFromFeatures: static_cast <MLDictionaryFeatureProvider * _Nonnull>(inFeatures)
431454                                                error: &error];
432455#endif 
433- 
456+          uint64_t  predictEndTime =  mach_absolute_time (); 
434457        Utils::handleError (error);
458+ 
459+         m_lastPredictDurationInNanoSeconds = convertMachTimeToNanoSeconds (predictEndTime - predictStartTime);
435460        return  Utils::featuresToDict (outFeatures);
436461    }
437462}
@@ -485,7 +510,7 @@ bool usingMacOS13OrHigher() {
485510}
486511#endif 
487512
488- py::list Model::batchPredict (const  py::list& batch) const   {
513+ py::list Model::batchPredict (const  py::list& batch) {
489514  @autoreleasepool {
490515      NSError * error = nil ;
491516
@@ -498,11 +523,14 @@ bool usingMacOS13OrHigher() {
498523      }
499524      MLArrayBatchProvider* batchProvider = [[MLArrayBatchProvider alloc ] initWithFeatureProviderArray:  array];
500525
526+       uint64_t  predictStartTime = mach_absolute_time ();
501527      //  Get predictions
502528      MLArrayBatchProvider* predictions = (MLArrayBatchProvider*)[m_model predictionsFromBatch: batchProvider
503529                                                                                         error: &error];
530+       uint64_t  predictEndTime = mach_absolute_time ();
504531      Utils::handleError (error);
505532
533+       m_lastPredictDurationInNanoSeconds = convertMachTimeToNanoSeconds (predictEndTime - predictStartTime);
506534      //  Convert predictions to output
507535      py::list ret;
508536      for  (int  i = 0 ; i < predictions.array .count ; i++) {
@@ -773,6 +801,22 @@ bool usingMacOS13OrHigher() {
773801    return  CoreML::MLMODEL_SPECIFICATION_VERSION_NEWEST;
774802}
775803
804+ py::object Model::getLoadDurationInNanoSeconds () const  {
805+     if  (m_loadDurationInNanoSeconds) {
806+         return  py::cast (m_loadDurationInNanoSeconds.value ());
807+     }
808+ 
809+     return  py::none ();
810+ }
811+ 
812+ py::object Model::getLastPredictDurationInNanoSeconds () const  {
813+     if  (m_lastPredictDurationInNanoSeconds) {
814+         return  py::cast (m_lastPredictDurationInNanoSeconds.value ());
815+     }
816+ 
817+     return  py::none ();
818+ }
819+ 
776820/* 
777821 * 
778822 * bindings 
@@ -788,6 +832,8 @@ bool usingMacOS13OrHigher() {
788832        .def (" predict"  , &Model::predict)
789833        .def (" batchPredict"  , &Model::batchPredict)
790834        .def (" get_compiled_model_path"  , &Model::getCompiledModelPath)
835+         .def (" get_load_duration_in_nano_seconds"  , &Model::getLoadDurationInNanoSeconds)
836+         .def (" get_last_predict_duration_in_nano_seconds"  , &Model::getLastPredictDurationInNanoSeconds)
791837        .def_static (" auto_set_specification_version"  , &Model::autoSetSpecificationVersion)
792838        .def_static (" maximum_supported_specification_version"  , &Model::maximumSupportedSpecificationVersion)
793839#if  BUILT_WITH_MACOS15_SDK
@@ -804,14 +850,18 @@ bool usingMacOS13OrHigher() {
804850    py::class_<State>(m, " _State"  , py::module_local ());
805851
806852#if  ML_COMPUTE_DEVICE_IS_AVAILABLE
807-     py::class_<CPUComputeDevice>(m, " _MLCPUComputeDeviceProxy"  , py::module_local ());
808-     py::class_<GPUComputeDevice>(m, " _MLGPUComputeDeviceProxy"  , py::module_local ());
853+     py::class_<CPUComputeDevice>(m, " _MLCPUComputeDeviceProxy"  , py::module_local ())
854+        .def (py::init ());
855+     py::class_<GPUComputeDevice>(m, " _MLGPUComputeDeviceProxy"  , py::module_local ())
856+        .def (py::init ());
809857    py::class_<NeuralEngineComputeDevice>(m, " _MLNeuralEngineComputeDeviceProxy"  , py::module_local ())
858+         .def (py::init ())
810859        .def (" get_total_core_count"  , &NeuralEngineComputeDevice::getTotalCoreCount);
811860#endif 
812861
813862#if  ML_COMPUTE_PLAN_IS_AVAILABLE
814863    py::class_<ComputePlan>(m, " _MLComputePlanProxy"  , py::module_local ())
864+         .def (py::init ())
815865        .def_property_readonly (" model_structure"  , &ComputePlan::getModelStructure)
816866        .def (" get_compute_device_usage_for_mlprogram_operation"  , &ComputePlan::getComputeDeviceUsageForMLProgramOperation)
817867        .def (" get_compute_device_usage_for_neuralnetwork_layer"  , &ComputePlan::getComputeDeviceUsageForNeuralNetworkLayer)
0 commit comments