jjacques68 Posté(e) le 6 juin 2020 Signaler Posté(e) le 6 juin 2020 hello !! Je me pose la question suivante, niveau performances (rapidité d'exécution, réactivité, autres ...) : j'essaye de prendre des précautions dans les termes choisi... faudrait-il mieux utiliser : par exemple : api.post("/devices/xxx/action/turnOn", {}) ou fibaro.call(xxx, "turnOn") autre exemple : api.get("/devices/xxx").properties.value ou fibaro.getValue(xxx, "value") je demande ça car j'ai l'impression qu'il y a une différence au niveau de la "latence" ou "réactivité", certes très légère... Mais quand les script commencent à devenir lourd, ça peut peut-être avoir son importance non ? J'ai envie de dire que passer par l'API semble plus rapide qu'utiliser les fonctions Fibaro ! Je me trompe ? c'est juste une impression ? Y a t il une règle spécifique ? Qu'en pensez vous ?
Lazer Posté(e) le 6 juin 2020 Signaler Posté(e) le 6 juin 2020 Je pense que tu as raison, car les commandes fibaro font appel à l'API il me semble bien. Donc ça rajoute une encapsulation. Le seul moyen de le savoir, c'est de faire un benchmark. Tu fais une boucle qui répète 1000 fois la même action, et tu mesures la durée d'exécution avec os.clock() Tu fais tourner chaque boucle 2 ou 3 fois pour absorber les variations, et tu compares les 2 résultats des différentes commandes
jjacques68 Posté(e) le 6 juin 2020 Auteur Signaler Posté(e) le 6 juin 2020 pas bête vais le faire, je posterai les résultats...
jjacques68 Posté(e) le 6 juin 2020 Auteur Signaler Posté(e) le 6 juin 2020 oh ben merde ! je m'y attendais pas à ça !!!! function QuickApp:button1(event) local t1 = os.time() self:trace("Start GET with API") for i = 1, 1000 do res = api.get("/devices/38").properties.value end self:trace("Get with API : ", os.difftime(os.time(),t1), "secondes") end function QuickApp:button2(event) local t1 = os.time() self:trace("Start GET with FIBARO") for i = 1, 1000 do res = fibaro.getValue(38, "value") end self:trace("Get with FIBARO : ", os.difftime(os.time(),t1), "secondes") end résultat : [06.06.2020] [11:38:57] [TRACE] [QUICKAPP600]: Start GET with API [06.06.2020] [11:39:10] [TRACE] [QUICKAPP600]: Get with API : 13.0 secondes [06.06.2020] [11:39:13] [TRACE] [QUICKAPP600]: Start GET with FIBARO [06.06.2020] [11:39:16] [TRACE] [QUICKAPP600]: Get with FIBARO : 3.0 secondes
Lazer Posté(e) le 6 juin 2020 Signaler Posté(e) le 6 juin 2020 Hum, étonnant. Cela me rappelle un vieux Benchmark développé par Steven lors du passage à la v4 sur HC2, qui démontrait que la HC3 en v3 était plus performante que la v4 : En page 7 de ce topic : Tu crois que tu pourrais le porter pour un QuickApp sur HC3 ? Et le même dans une scène, pour comparer les performances. D'ailleurs c'est pareil pour ton test, il faudrait le faire tourner dans une scène pour voir s'il subsiste une différence comme sur la HC2
jjacques68 Posté(e) le 6 juin 2020 Auteur Signaler Posté(e) le 6 juin 2020 et pour le CALL : function QuickApp:button3(event) local t1 = os.time() self:trace("Start CALL with API") for i = 1, 1000 do res = api.post("/devices/203/action/turnOn",{}) end self:trace("CALL with API : ", os.difftime(os.time(),t1), "secondes") end function QuickApp:button4(event) local t1 = os.time() self:trace("Start CALL with FIBARO") for i = 1, 1000 do res = fibaro.call(203, "turnOn") end self:trace("CALL with FIBARO : ", os.difftime(os.time(),t1), "secondes") end résultat : [06.06.2020] [11:48:05] [TRACE] [QUICKAPP600]: Start CALL with API [06.06.2020] [11:49:30] [TRACE] [QUICKAPP600]: CALL with API : 85.0 secondes [06.06.2020] [11:49:36] [TRACE] [QUICKAPP600]: Start CALL with FIBARO [06.06.2020] [11:51:04] [TRACE] [QUICKAPP600]: CALL with FIBARO : 88.0 secondes donc ça c’était depuis un QA, je vais essayer depuis une scène...
jjacques68 Posté(e) le 6 juin 2020 Auteur Signaler Posté(e) le 6 juin 2020 Alors dans une scène : déjà la fonction os.difftime() est pas dispo par grave, j'ai contourné... voilà le résultat : [06.06.2020] [12:03:01] [TRACE] [TEST SCENE]: Start GET with API [06.06.2020] [12:03:21] [TRACE] [TEST SCENE]: Get with API : 20 secondes [06.06.2020] [12:03:21] [TRACE] [TEST SCENE]: Start GET with FIBARO [06.06.2020] [12:03:24] [TRACE] [TEST SCENE]: Get with FIBARO : 3 secondes ---------------------------------------------------------------------------- [06.06.2020] [12:03:24] [TRACE] [TEST SCENE]: Start CALL with API [06.06.2020] [12:04:58] [TRACE] [TEST SCENE]: CALL with API : 94 secondes [06.06.2020] [12:04:58] [TRACE] [TEST SCENE]: Start CALL with FIBARO [06.06.2020] [12:06:15] [TRACE] [TEST SCENE]: CALL with FIBARO : 77 secondes
jjacques68 Posté(e) le 6 juin 2020 Auteur Signaler Posté(e) le 6 juin 2020 (modifié) rapide conclusion de ce test, si on peut en faire une à ce stade, il faut mieux utiliser les commande Fibaro, du moins pour le GET !! j'aurai jamais pensé ça ! Modifié le 6 juin 2020 par jjacques68 1
jjacques68 Posté(e) le 6 juin 2020 Auteur Signaler Posté(e) le 6 juin 2020 @Lazer : j'ai repris le code de @Steven comme tu proposais : os.clock() n'existe pas, il me retourne une erreur, je l'ai donc enlevé dommage... -- Parameters -- local NameScene = "Test" local id_exist = 600 local global_exist = "DelayVmc" local nbIteration = 1000 -- Do not touch please --- local id_not_exist = 100056 local global_not_exist = "AABBCCDDEEFFGGHHIIFFF" function log(name, start) if (start) then fibaro.trace(NameScene,string.format("%s instruction time : %ds", name, os.time()-start)) else fibaro.trace(NameScene,string.format("%s", name)) end end function execute(name, func) if not pcall(function() local start = os.time() for i= 1, nbIteration do func() end log(name, start) end) then fibaro.warning(NameScene, "ERROR : " .. name) end end log("Nb runs : " .. nbIteration .. " | id : " .. id_exist .. " | G.Variable : " .. global_exist) log("----------------------------------------------") log("") -- Tests --- execute("getValue Exist_________________:", function() fibaro.getValue(id_exist, "value") end) execute("getValue Not Exist_____________:", function() fibaro.getValue(id_not_exist, "value") end) execute("turnOn_________________________:", function() fibaro.call(id_exist, "turnOn") end) execute("getGlobalVariable Exist________:", function() fibaro.getGlobalVariable(global_exist) end) execute("getGlobalVariable Not Exist____:", function() fibaro.getGlobalVariable(global_not_exist) end) execute("setGlobalVariable______________:", function() fibaro.setGlobalVariable(global_exist, fibaro.getGlobalVariable(global_exist)) end) execute("getType________________________:", function() fibaro.getType(id_exist) end) execute("getName________________________:", function() fibaro.getName(id_exist) end) execute("getRoomID______________________:", function() fibaro.getRoomID(id_exist) end) execute("getRoomName____________________:", function() fibaro.getRoomName(fibaro.getRoomID(id_exist)) end) execute("getSunrise_____________________:", function() fibaro.getValue(1, "sunsetHour") end) log("") log("----------------------------------------------") log("ALL DONE") Résultat : [06.06.2020] [17:31:04] [TRACE] [TEST]: Nb runs : 1000 | id : 600 | G.Variable : DelayVmc [06.06.2020] [17:31:04] [TRACE] [TEST]: ---------------------------------------------- [06.06.2020] [17:31:04] [TRACE] [TEST]: [06.06.2020] [17:31:08] [TRACE] [TEST]: getValue Exist_________________: instruction time : 4s [06.06.2020] [17:31:11] [TRACE] [TEST]: getValue Not Exist_____________: instruction time : 3s [06.06.2020] [17:32:11] [TRACE] [TEST]: turnOn_________________________: instruction time : 60s [06.06.2020] [17:32:11] [TRACE] [TEST]: getGlobalVariable Exist________: instruction time : 0s [06.06.2020] [17:32:11] [TRACE] [TEST]: getGlobalVariable Not Exist____: instruction time : 0s [06.06.2020] [17:32:11] [TRACE] [TEST]: setGlobalVariable______________: instruction time : 0s [06.06.2020] [17:32:25] [TRACE] [TEST]: getType________________________: instruction time : 14s [06.06.2020] [17:32:37] [TRACE] [TEST]: getName________________________: instruction time : 12s [06.06.2020] [17:32:50] [TRACE] [TEST]: getRoomID______________________: instruction time : 13s [06.06.2020] [17:33:07] [TRACE] [TEST]: getRoomName____________________: instruction time : 17s [06.06.2020] [17:33:10] [TRACE] [TEST]: getSunrise_____________________: instruction time : 3s [06.06.2020] [17:33:10] [TRACE] [TEST]: [06.06.2020] [17:33:10] [TRACE] [TEST]: ---------------------------------------------- [06.06.2020] [17:33:10] [TRACE] [TEST]: ALL DONE
Lazer Posté(e) le 6 juin 2020 Signaler Posté(e) le 6 juin 2020 Top ça, merci Sur mon HC3, qui semble un peu plus rapide que la tienne, surement parce qu'elle est moins chargée, elle ne me sert que pour du développement. [06.06.2020] [19:04:35] [TRACE] [TEST LUA PERF]: Nb runs : 1000 | id : 22 | G.Variable : Test [06.06.2020] [19:04:35] [TRACE] [TEST LUA PERF]: ---------------------------------------------- [06.06.2020] [19:04:35] [TRACE] [TEST LUA PERF]: [06.06.2020] [19:04:37] [TRACE] [TEST LUA PERF]: getValue Exist_________________: instruction time : 2s [06.06.2020] [19:04:39] [TRACE] [TEST LUA PERF]: getValue Not Exist_____________: instruction time : 2s [06.06.2020] [19:05:10] [TRACE] [TEST LUA PERF]: turnOn_________________________: instruction time : 31s [06.06.2020] [19:05:10] [TRACE] [TEST LUA PERF]: getGlobalVariable Exist________: instruction time : 0s [06.06.2020] [19:05:10] [TRACE] [TEST LUA PERF]: getGlobalVariable Not Exist____: instruction time : 0s [06.06.2020] [19:05:10] [TRACE] [TEST LUA PERF]: setGlobalVariable______________: instruction time : 0s [06.06.2020] [19:05:22] [TRACE] [TEST LUA PERF]: getType________________________: instruction time : 12s [06.06.2020] [19:05:35] [TRACE] [TEST LUA PERF]: getName________________________: instruction time : 13s [06.06.2020] [19:05:47] [TRACE] [TEST LUA PERF]: getRoomID______________________: instruction time : 12s [06.06.2020] [19:06:02] [TRACE] [TEST LUA PERF]: getRoomName____________________: instruction time : 15s [06.06.2020] [19:06:03] [TRACE] [TEST LUA PERF]: getSunrise_____________________: instruction time : 1s [06.06.2020] [19:06:03] [TRACE] [TEST LUA PERF]: [06.06.2020] [19:06:03] [TRACE] [TEST LUA PERF]: ---------------------------------------------- [06.06.2020] [19:06:03] [TRACE] [TEST LUA PERF]: ALL DONE Pour info sur mon HC2 : [DEBUG] 11:39:36: Nb runs : 1000 | id : 11 | G.Variable : SMS [DEBUG] 11:39:36: ---------------------------------------------- [DEBUG] 11:39:36: [DEBUG] 11:39:40: getValue Exist : instruction time : 4s | cpu time : 1.726s [DEBUG] 11:39:44: getValue Not Exist : instruction time : 4s | cpu time : 1.40616s [DEBUG] 11:39:55: setValue : instruction time : 11s | cpu time : 3.4183s [DEBUG] 11:39:58: getGlobal Exist : instruction time : 3s | cpu time : 1.73315s [DEBUG] 11:40:01: getGlobal Not Exist : instruction time : 3s | cpu time : 1.41096s [DEBUG] 11:40:46: setGlobal : instruction time : 45s | cpu time : 3.78558s [DEBUG] 11:40:56: getType : instruction time : 10s | cpu time : 6.68276s [DEBUG] 11:41:06: getName : instruction time : 10s | cpu time : 6.87206s [DEBUG] 11:41:16: getRoomID : instruction time : 10s | cpu time : 6.71876s [DEBUG] 11:41:29: getRoomName : instruction time : 12s | cpu time : 8.48164s [DEBUG] 11:41:32: getSunrise : instruction time : 4s | cpu time : 1.69301s [DEBUG] 11:41:32: [DEBUG] 11:41:32: ---------------------------------------------- [DEBUG] 11:41:32: ALL DONE Les résultats sont pour le moins surprenants : dans l'ensemble, mon HC2 de prod, donc avec déjà une bonne activité (gestion des modules Z-Wave, VD, Scènes, ....) est plus performante que la HC3 !!! Ça c'est pas banal.... Imagine la HC2 en v3 comment ça décoiffait, elle explose tout le monde 1 1
jjacques68 Posté(e) le 6 juin 2020 Auteur Signaler Posté(e) le 6 juin 2020 ben suis entrain de me demander si c'est parce qu'elle est chargée ou parce que je code comme un pied
Lazer Posté(e) le 7 juin 2020 Signaler Posté(e) le 7 juin 2020 Je suis en train de penser à autre chose en regardant les CPU de la HC3. Elle a 4 coeurs, tandis que la HC2 n'en avait que 2. Chaque QuickApp ou scène est monothreadé, c'est à dire que son code ne s'exécute que sur un seul cœur. Donc ce benchmark est limité par la puissance d'un cœur. En fait c'est même plus compliqué, car il fait appel à l'API et aux fonctions Fibaro qui sont exécutées dans d'autres processus, sur d'autres cœurs. Bref, il ne permet pas de comparer les performances d'une HC2 avec une HC3. Mais le raisonnement reste valable pour optimiser son propre code LUA
jjacques68 Posté(e) le 7 juin 2020 Auteur Signaler Posté(e) le 7 juin 2020 pas moyen de savoir quel cœur est utilisé ? je me suis posé ces questions de performances, suite à mes problèmes liés au manque de multi instances des scènes. Un gars sur le fofo officiel m'a proposé d'utilité l'API RefreshStates. Qui recense tous les événements. Je l'interroge toutes les 200 ms. selon des filtres que j'ai mis en place, je lance des fonctions dans des QA. Du coup, avec ce procédé, je ne passe plus par des scènes triggées. Et j'ai donc résolu le problème d'événements "loupé" ou plutôt d'actions non effectuées. Mais ! je constate une légère latence dans les actions...
Lazer Posté(e) le 7 juin 2020 Signaler Posté(e) le 7 juin 2020 Non pas vraiment, et de toute façon ça n'aurait que peu d'intérêt, le scheduleur dans le noyau de l'OS affecte les threads d'exécution dynamiquement aux coeurs, en fonction de la charge. C'est le principe même d'un OS multithreadé (par opposition à.... MS-DOS ) Tu peux regarder l'onglet CPU du panneau de Diagnostiques pendant l'exécution de ta scène de Benchmark, tu verras que tous les cœurs travaillent, justement parce que l'OS équilibre au mieux l'activité. Ce qu'il faut plusieurs dizaines de fois par seconde, donc c'est invisible à l'échelle humaine.
jjacques68 Posté(e) le 7 juin 2020 Auteur Signaler Posté(e) le 7 juin 2020 oui j'avais observé ça, les 4 cœurs montent en flèches
Krikroff Posté(e) le 12 juin 2020 Signaler Posté(e) le 12 juin 2020 Le 06/06/2020 à 11:08, Lazer a dit : les commandes fibaro font appel à l'API il me semble bien. C'est tout à fait ça... Et certainement encore le cas sur HC3 Les fonctions fibaro ne sont qu'une surcouche sur des fonctions LUA internes qui utilisent des libs lua, dans le but de simplifier la vie de monsieur tout le monde. Par ex pour un getValue le parcours est le suivant: <-> fibaro.getValue <-> fibaro:get + des vérifications + conversion des valeurs retournées (string: valeur + modification) <-> api.get <-> http:GET() => pas possible de descendre plus bas sauf en root <-> lib @jjacques68 Petite piste d'optimisation Le api.get dans le cas d'un getValue devrait être : api.get("/devices/{deviceID}/properties/{propertyName}") et pas api.get("/devices/{deviceID}").properties.value Tu vas comprendre http://hc3-00000000/api/devices/27/properties/value retourne { "value": 0, "modified": 1591920284 } http://hc3-00000000/api/devices/27 retourne l’intégralité de l'objet "id": 27, "name": "27", "roomID": 220, "view": [ { "assetsPath": "/dynamic-plugins/com.fibaro.multilevelSwitch/assets", "jsPath": "/dynamic-plugins/com.fibaro.multilevelSwitch", "name": "com.fibaro.multilevelSwitch", "translatesPath": "/dynamic-plugins/com.fibaro.multilevelSwitch/i18n", "type": "ts" } ], "type": "com.fibaro.multilevelSwitch", "baseType": "com.fibaro.binarySwitch", "enabled": true, "visible": true, "isPlugin": false, "parentId": 492, "viewXml": false, "configXml": false, "interfaces": [ "deviceGrouping", "levelChange", "light", "power", "zwave", "zwaveSwitchAll" ], "properties": { "parameters": [ { "id": 1, "lastReportedValue": 255, "lastSetValue": 255, "size": 1, "value": 255 }, { "id": 6, "lastReportedValue": 0, "lastSetValue": 0, "size": 1, "value": 0 }, { "id": 7, "lastReportedValue": 1, "lastSetValue": 1, "size": 1, "value": 1 }, { "id": 8, "lastReportedValue": 1, "lastSetValue": 1, "size": 1, "value": 1 }, { "id": 9, "lastReportedValue": 5, "lastSetValue": 5, "size": 1, "value": 5 }, { "id": 10, "lastReportedValue": 1, "lastSetValue": 1, "size": 1, "value": 1 }, { "id": 11, "lastReportedValue": 1, "lastSetValue": 1, "size": 1, "value": 1 }, { "id": 12, "lastReportedValue": 99, "lastSetValue": 99, "size": 1, "value": 99 }, { "id": 13, "lastReportedValue": 2, "lastSetValue": 2, "size": 1, "value": 2 }, { "id": 14, "lastReportedValue": 0, "lastSetValue": 0, "size": 1, "value": 0 }, { "id": 15, "lastReportedValue": 1, "lastSetValue": 1, "size": 1, "value": 1 }, { "id": 16, "lastReportedValue": 1, "lastSetValue": 1, "size": 1, "value": 1 }, { "id": 17, "lastReportedValue": 0, "lastSetValue": 0, "size": 1, "value": 0 }, { "id": 18, "lastReportedValue": 0, "lastSetValue": 0, "size": 1, "value": 0 }, { "id": 19, "lastReportedValue": 0, "lastSetValue": 0, "size": 1, "value": 0 }, { "id": 20, "lastReportedValue": 110, "lastSetValue": 110, "size": 1, "value": 110 }, { "id": 30, "lastReportedValue": 3, "lastSetValue": 3, "size": 1, "value": 3 }, { "id": 39, "lastReportedValue": 600, "lastSetValue": 600, "size": 2, "value": 600 } ], "pollingTimeSec": 0, "zwaveCompany": "Fibargroup", "zwaveInfo": "3,3,20", "zwaveVersion": "1.4", "categories": [ "lights" ], "configured": true, "dead": false, "deadReason": "", "deviceControlType": 23, "deviceGroup": [ ], "deviceGroupMaster": 0, "deviceIcon": 15, "emailNotificationID": 3, "emailNotificationType": 0, "endPointId": 0, "isLight": true, "log": "", "logTemp": "", "manufacturer": "", "markAsDead": true, "model": "", "nodeId": 8, "parametersTemplate": "1", "power": 0.0, "powerConsumption": 80, "productInfo": "1,15,1,0,1,4,1,4", "pushNotificationID": 4, "pushNotificationType": 0, "remoteGatewayId": 0, "saveLogs": true, "serialNumber": "", "showEnergy": true, "smsNotificationID": 3, "smsNotificationType": 0, "state": false, "switchAllMode": "SWITCH_ALL_INCLUDED_IN_THE_ALL_ON_ALL_OFF_FUNCTIONALITY", "useTemplate": true, "userDescription": "", "value": 0 }, "actions": { }, "created": 1591920284, "modified": 1591920284, "sortOrder": 5 } Donc si tu veux réellement gagner du CPU, économiser la mémoire et améliorer les temps de réponse je crois que tu sais ce qu'il te reste à faire. Dans le cas d'un fibaro.call bien que cela puisse paraître dingue ceci https://www.domotique-fibaro.fr/topic/14144-script-custom-callaction/ est vraisemblablement la méthode la plus souple et la plus efficace en matière de performance mais à mettre dans la balance avec les lignes de codes en sus à charger à l'appel d'une scène ou en mémoire pour un QA, tout est toujours une affaire de compromis et une solution adaptée a un cas d'usage 2
jjacques68 Posté(e) le 12 juin 2020 Auteur Signaler Posté(e) le 12 juin 2020 @Krikroff : mais oui biensûr !! je n'y avais pas penser pour le get !! Directement cibler la propriété que l'on souhaite dans la commande ! J'essaye ça ce soir ! oulala, va y avoir de l'optimisation de code dans l'air ce weekend... 1
jjacques68 Posté(e) le 12 juin 2020 Auteur Signaler Posté(e) le 12 juin 2020 Il y a 14 heures, Krikroff a dit : Le api.get dans le cas d'un getValue devrait être : api.get("/devices/{deviceID}/properties/{propertyName}") Alors peut on accéder de la même manière à d'autre info qui ne sont pas dans "properties" ? comme par exemple le roomID d'un device ? J'ai essayé ça mais ça ne marche pas : local RoomID = api.get("/devices/203/roomID")
Messages recommandés