{"id":3888,"date":"2020-04-02T18:21:59","date_gmt":"2020-04-02T18:21:59","guid":{"rendered":"http:\/\/www.colorwave.it\/blog\/?p=3888"},"modified":"2020-04-02T18:23:36","modified_gmt":"2020-04-02T18:23:36","slug":"i-timer-classi-per-la-valutazione-delle-performance-del-software","status":"publish","type":"post","link":"https:\/\/www.colorwave.it\/blog\/framework\/i-timer-classi-per-la-valutazione-delle-performance-del-software\/admin\/2020\/04\/","title":{"rendered":"I Timer: classi per la valutazione delle performance del software"},"content":{"rendered":"<p>Il namespace CLKS contiene tre classi utilizzate per la gestione dei tempi di esecuzione del codice:<\/p>\n<ul>\n<li><span style=\"color: #008080;\"><strong>Clock<\/strong> <\/span>: utilizzata per valutare il trascorrere del tempo con la risoluzione massima di un secondo; \u00e8 usata per gestire processi lenti, ad esempio gli allarmi legati ad eventi che devono avvenire in x minuti.<\/li>\n<li><span style=\"color: #008080;\"><strong>Clock_hight_resol<\/strong><\/span>:\u00a0 \u00e8 usata per gestire timers con risoluzione dei tempi nell&#8217;ordine dei nanosecondi<\/li>\n<li><span style=\"color: #008080;\"><strong>PerformanceCounter<\/strong> <\/span>: \u00e8 usata per semplificare la gestione dei timers, usa internamente i clock hardware dei processori, usando il numero di tick di esecuzione e la frequenza del clock.<\/li>\n<\/ul>\n<h3>Clock<\/h3>\n<pre class=\"lang:default decode:true\">\/** \\brief  crea un timer e lo lancia \r\n#define $_clock_start Clock __ck; __ck.Start();\r\n\r\n\/** \\brief visualizza la finestra con il tempo trascorso\r\n*\/\r\n#define $_clock_end(msg) __ck.DisplayElapsedTime(msg);\r\n\r\n\/** rimanda il numero di millisecondi trascorsi\r\n*\/\r\n#define $_clock_elapsed_msec(clk,msec) { CTimeSpan t=clk.ElapsedTime(); msec=(t.GetHours()*3600+ t.GetMinutes()*60+t.GetSeconds())*1000; }\r\n\r\n\r\n\tclass AFX_EXT_CLASS Clock\r\n\t{\r\n\tpublic:\r\n\t\tbool TimeOut();\r\n\t\tCTimeSpan ElapsedTime();\r\n\t\tvoid SetTimeout(int h, int min, int secondi);\r\n\t\tvoid Start();\r\n\t\tClock();\r\n\t\tvirtual ~Clock();\r\n\r\n\t\tvoid DisplayElapsedTime(TCHAR *msg = 0);\r\n\r\n\r\n\tprivate:\r\n\t\tint timeout;\t\t\t\t\/\/ timeout in secondi;\r\n\t\tCTime startTime;\t\t\t\/\/ inizio del conteggio\r\n\t};\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #008000;\"><strong>void SetTimeout(int h, int min, int secondi)<\/strong> <\/span>: Definisce il timeout per il successivo test (vedi TimeOut()).<\/p>\n<p><span style=\"color: #008000;\"><strong>void\u00a0 Start() :\u00a0<\/strong><\/span> lancia il timer in esecuzione.<\/p>\n<p><span style=\"color: #008000;\"><strong>CTimeSpan ElapsedTime()<\/strong><\/span> : rimanda l&#8217;intervallo di tempo trascorso da Start()<\/p>\n<p><span style=\"color: #008000;\"><strong>bool TimeOut()<\/strong><\/span> : rimanda true se \u00e8 trascorso l&#8217;intervallo di tempo definito usando SetTimeOut(..)<\/p>\n<p><strong><span style=\"color: #008000;\">DisplayElapsedTime(TCHAR *msg)<\/span><\/strong> : visualizza una finestra modale contenente il messaggio msg associato al tempo trascorso (in h, m, sec);<\/p>\n<p>Sono fornite inoltre tre macro, parte di CLB_language, per semplificare la scrittura del codice:\u00a0<span style=\"color: #008000;\"><strong><em>$_clock_start, $_clock_end(msg) , $_clock_elapsed_msec(clk,msec)\u00a0<\/em><\/strong><\/span><\/p>\n<p>L&#8217;esempio di codice che segue mostra come utilizzare queste macro:<\/p>\n<pre class=\"lang:default decode:true\">bool calc_calib()\r\n\t{\r\n\t\tif (!calib)\r\n\t\t{\r\n\t\t\t__warning(__translate(\"You must load the calibration image reference before calculation..\"))\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t$_clock_start\r\n\r\n\t\t\tMat *view = Cv_functions::Image_2_mat(calib);\t\t\/\/ alloca la matrice \r\n\t\t\tif (view)\r\n\t\t\t{\r\n\t\t\t\t$_clock_start\r\n\t\t\t\tint chessBoardFlags = CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE | CALIB_CB_FAST_CHECK;\r\n\r\n\t\t\t\t \/\/ .. se possibile usa openCL\r\n\t\t\t\tUMat uview = view-&gt;getUMat(cv::ACCESS_READ);\r\n\t\t\t\tbool found = findChessboardCorners(uview, vcs.boardSize, gpt, chessBoardFlags);\r\n\t\t\t\t\/\/\t\tbool found = findChessboardCorners(*view, vcs.boardSize, gpt, chessBoardFlags);\r\n\t\t\t\t$_clock_end(L\"ricerca dei punti della griglia\")\r\n\r\n\t\t\t\t__delete(view);\r\n\r\n\t\t\t\tif (found)\r\n\t\t\t\t\treturn draw_points();\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn false;\r\n\t}<\/pre>\n<p>il codice compreso fra $_clock_start e $_clock_end(L&#8221;ricerca dei punti della griglia&#8221;) \u00e8 quello testato dal timer; $_clock_end quindi visualizza il tempo trascorso per l&#8217;esecuzione.<br \/>\n<!--nextpage-->La classe Clock_hight_resol sfrutta alcune classi di timer definite dal odice C++11, e permette di valutare una granulazione dei tempi dell&#8217;ordine dei nanosecondi.<\/p>\n<pre class=\"lang:default decode:true\">\/**  risoluzione scala del tempo da usare\r\n\t*\/\r\n\ttypedef enum {\r\n\t\thour =0,\r\n\t\tmin,\r\n\t\tsec,\r\n\t\tmillisec,\r\n\t\tmicrosec,\r\n\t\tnanosec\r\n\t} time_resolution;\r\n\r\n\tclass Clock_hight_resol\r\n\t{\r\n\tpublic:\r\n\t\tClock_hight_resol(); \r\n\t\t~Clock_hight_resol();\r\n\r\n\t\t\/** \\brief Reset lancio del cronometro\r\n\t\t*\/\r\n\t\tinline void Start()\r\n\r\n\t\t\/** \\brief rimanda il tempo trascorso da Start() in msec effettivi\r\n\t\t*\/\r\n\t\tinline long long ElapsedTime(time_resolution tick=millisec)\r\n\t};<\/pre>\n<p><strong><span style=\"color: #008000;\">Start()\u00a0<\/span> <\/strong>lancia il timer in esecuzione<\/p>\n<p><strong><span style=\"color: #008000;\">ElapsedTime()<\/span> <\/strong>rimanda il tempo trascorso espresso nell&#8217;unit\u00e0 di misura passata (time_resolution passata)<\/p>\n<p>&nbsp;<\/p>\n<p><!--nextpage-->La classe PerformanceCounter sfrutta i clock hardware presenti in num di cicli di esecuzione. I timestamp e le misurazioni degli intervalli di tempo sono parte integrante delle misurazioni delle prestazioni del computer e della rete. Queste operazioni di misurazione delle prestazioni includono il calcolo del tempo di risposta, della velocit\u00e0 effettiva e della latenza, nonch\u00e9 l&#8217;esecuzione del codice di profilazione. Ciascuna di queste operazioni comporta una misurazione delle attivit\u00e0 che si verificano durante un intervallo di tempo definito da un evento di inizio e fine che pu\u00f2 essere indipendente da qualsiasi riferimento esterno dell&#8217;ora del giorno.<\/p>\n<pre class=\"lang:default decode:true \">\tclass AFX_EXT_CLASS PerformanceCounter\r\n\t{\r\n\tpublic:\r\n\t\tPerformanceCounter(void);\r\n\t\tvirtual ~PerformanceCounter(void);\r\n\r\n\t\t\/\/ inizia misura ciclo;\r\n\t\tvoid Start(void);\r\n\r\n\t\t\/\/ fine del ciclo: il numero di cicli utilizzati fra Start() ed End() vengono sommati al \r\n\t\t\/\/ totalizzatore num_cicli;\r\n\t\tvoid End(void);\r\n\r\n\t\t\/\/ rimanda il numero medio di cicli per iterazione testata\r\n\t\tint  NumMedioCicli();\r\n\r\n\t\t\/\/ rimanda il numero di iterazioni testate\r\n\t\tunsigned int  NumTest();\r\n\r\n\t\tfloat Secondi();\r\n\r\n\t\t\/\/ rimanda una stringa html con le informazioni sullo stato del counter\r\n\t\tSTRING Get_info();\r\n\r\n\t\t\/\/ display dello stato del counter\r\n\t\tvoid  Display();\r\n\r\n\t\t\/\/ azzera lo stato del counter;\r\n\t\t\/\/\r\n\t\tvoid Reset();\r\n\r\n\t\t\/** rimanda un nuovo elemento\r\n\t\t*\/\r\n\t\tstatic PerformanceCounter *Create(\r\n\t\t\tSTRING name\t\t\/\/\/&lt; Nome assegnato\r\n\t\t);\r\n\r\n\t\t\/** visualizza le statistiche di tutti i PerformanceCounter attivi \r\n\t\t*\/\r\n\t\tstatic void DisplayStats();\r\n\r\n\t\t\/** distrugge tutti i rilevatori di performance attivi nel software\r\n\t\t*\/\r\n\t\tstatic void Destroy();\r\n\r\n\t\t__use_private_struct\r\n\t};\r\n<\/pre>\n<p>La classe, attraverso la funzione statica Create() gestisce una lista di PerformanceCounter, a ciascuno dei quali \u00e8 assegnato un nome. L&#8217;uso pi\u00f9 comune della classe \u00e8 la gestione di un insieme di timer, ciascuno associato all&#8217;esecuzione di un thread, prevedendo che nel corso dell&#8217;esecuzione del codice, ciascun thread possa essere chiamato pi\u00f9 volte. Se il thread contiene un blocco di codice come il seguente<\/p>\n<pre class=\"lang:default decode:true\">\t\r\n.....\r\n\t$_pc_start(pfc);\r\n\r\n.....\r\n..... blocco di codice da eseguire\r\n....\t\t\r\n\t$_pc_end(pfc);\r\n\r\n<\/pre>\n<p>dove <em><span style=\"color: #008000;\">pfc<\/span><\/em> \u00e8 un PerformanceCounter * ottenuto in fase di inizializzazione mediante la funzione statica <em><span style=\"color: #008000;\">PerformanceCounter ::Create(L&#8221;my_timer_name&#8221;)<\/span><\/em>, la<em><span style=\"color: #008000;\"> $_pc_end(pfc)<\/span><\/em> memorizza il numero di cicli di esecuzione del blocco iniziato da <span style=\"color: #008000;\"><em>$_pc_start(pcf),<\/em><\/span> ed incrementa il numero di volte di esecuzione del blocco.<\/p>\n<p>A fine utilizzo il programmatore potr\u00e0 ricavare le informazioni di esecuzione del blocco richiamando la funzione statica <em><span style=\"color: #008000;\">PerformanceCounter ::DisplayStats()<\/span><\/em>, che visualizzer\u00e0 la statistica d&#8217;uso di tutti i blocchi di codice sui quali \u00e8 definito un PerformanceCounter (nel nostro caso pcf).<\/p>\n<p>Per semplificare la gestione dei timer, vengono in genere usate alcune\u00a0 macro di <span style=\"color: #008000;\"><em>CLB_language<\/em><\/span>.<\/p>\n<pre class=\"lang:default decode:true\">#define $_pc_reset(p)  \tif (p)\tp-&gt;Reset();\r\n\r\n#define $_pc_create(p,tit) \tp = CLKS::PerformanceCounter::Create(tit);\r\n\r\n#define $_pc_start(p)\tif (p) p-&gt;Start();\r\n#define $_pc_end(p)\t\tif (p) p-&gt;End();\r\n\r\n#define $_pc_display_statistics   CLKS::PerformanceCounter::DisplayStats();\r\n\r\n#define $_pc_destroy_all\t\t  CLKS::PerformanceCounter::Destroy();\r\n<\/pre>\n<p>Le macro richiamano le funzioni della classe per dichiarare, inizializzare e gestire un insieme di PerformanceCounter;\u00a0 per esempio, supponiamo di dovere analizzare le performance di tre diversi blocchi di esecuzione di codice. per prima cosa, in fase di inizializzazione definiremo\u00a0 da qualche parte i timer necessari:<\/p>\n<pre class=\"lang:default decode:true \">...\r\n...\r\n\r\n\/\/ PerformanceCounter declarations...\r\n\r\nCLKS::PerformanceCounter *p1 = 0, *p2 = 0, *p3 = 0;\r\n\r\n...\r\n...\r\n<\/pre>\n<p>quindi, in un successivo pezzo di codice creeremo le tre istanze<\/p>\n<pre class=\"lang:default decode:true\">...\r\n...\r\n\r\n\/\/ create 3 PerformanceCounter ...\r\n\r\n$_pc_create(p1, L\" total calculation timecode\")\r\n$_pc_create(p2, L\" using kernel\")\r\n$_pc_create(p3, L\" file write\")\r\n\r\n...\r\n...<\/pre>\n<p>La $_pc_create genera l&#8217;istanza PerformanceCounter\u00a0 ed azzera i contatori interni (PerformanceCounter\u00a0 ::Reset())<\/p>\n<p>successivamente, se necessario, sar\u00e0 possibile inizializzare nuovamente un&#8217;istanza\u00a0 utilizzando la macro $_pc_reset:<\/p>\n<pre class=\"lang:default decode:true\">\/\/ if you want to reset internal counters..\r\n..\r\n$_pc_reset(p1)\r\n..\r\n$_pc_reset(p2)\r\n..\r\n$_pc_reset(p3)<\/pre>\n<p>Quindi, di seguito sono riportati gli schemi di tre blocchi di codice, ciascuno testato attraverso i tre PerformanceCounter\u00a0 p1,p2,p3. Non \u00e8 definita la posizione dei tre blocchi, che potrebbero appartenere a thread diversi o a funzioni diverse..<\/p>\n<pre class=\"lang:default decode:true \">...\r\n...\r\n\/\/ block code 1\r\n{\r\n\t$_pc_start(p1)\r\n\t\t...\t\t\/\/ code to test..\t\t\r\n\t\t...\r\n\t$_pc_end(p1)\r\n}\r\n...\r\n...\r\n\/\/ block code 2\r\n{\r\n\t$_pc_start(p2)\r\n\t\t...\t\t\/\/ code to test..\t\t\r\n\t\t...\r\n\t$_pc_end(p2)\r\n}\r\n...\r\n...\r\n\/\/ block code 3\r\n{\r\n\t$_pc_start(p3)\r\n\t\t...\t\t\/\/ code to test..\t\t\r\n\t\t...\r\n\t$_pc_end(p3)\r\n}\r\n\r\n..\r\n..<\/pre>\n<p>Ciascun blocco inizia con la macro <span style=\"color: #008000;\"><em>$_pc_start(px)<\/em>\u00a0<\/span> e termina con <em><span style=\"color: #008000;\">$_pc_end(px).<\/span><\/em> In questo modo\u00a0 Il codice tiene cos\u00ec traccia di quante volte \u00e8 stato eseguito un blocco e dei tempi di calcolo relativi. Terminato l&#8217;uso, quando si riterr\u00e0 necessario, potr\u00e0 essere visualizzato lo stato dell&#8217;esecuzione mediante la macro <em><span style=\"color: #008000;\">$_pc_display_statistics<\/span><\/em><\/p>\n<pre class=\"lang:default decode:true\">...\r\n\r\n$_pc_display_statistics\r\n\r\n...<\/pre>\n<p>che produrr\u00e0 una finestra del tipo:<\/p>\n<p><img fetchpriority=\"high\" decoding=\"async\" data-attachment-id=\"3900\" data-permalink=\"https:\/\/www.colorwave.it\/blog\/framework\/i-timer-classi-per-la-valutazione-delle-performance-del-software\/admin\/2020\/04\/attachment\/performance_counter\/\" data-orig-file=\"https:\/\/i0.wp.com\/www.colorwave.it\/blog\/wp-content\/uploads\/2020\/04\/performance_counter.jpg?fit=394%2C280&amp;ssl=1\" data-orig-size=\"394,280\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"performance_counter\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/i0.wp.com\/www.colorwave.it\/blog\/wp-content\/uploads\/2020\/04\/performance_counter.jpg?fit=300%2C213&amp;ssl=1\" data-large-file=\"https:\/\/i0.wp.com\/www.colorwave.it\/blog\/wp-content\/uploads\/2020\/04\/performance_counter.jpg?fit=394%2C280&amp;ssl=1\" tabindex=\"0\" role=\"button\" class=\"alignleft size-full wp-image-3900\" src=\"https:\/\/i0.wp.com\/www.colorwave.it\/blog\/wp-content\/uploads\/2020\/04\/performance_counter.jpg?resize=394%2C280&#038;ssl=1\" alt=\"\" width=\"394\" height=\"280\" srcset=\"https:\/\/i0.wp.com\/www.colorwave.it\/blog\/wp-content\/uploads\/2020\/04\/performance_counter.jpg?w=394&amp;ssl=1 394w, https:\/\/i0.wp.com\/www.colorwave.it\/blog\/wp-content\/uploads\/2020\/04\/performance_counter.jpg?resize=300%2C213&amp;ssl=1 300w\" sizes=\"(max-width: 394px) 100vw, 394px\" data-recalc-dims=\"1\" \/><\/p>\n<p>La finestra visualizzer\u00e0 le informazioni relative ai <span style=\"color: #008000;\"><em>PerformanceCounter<\/em> <\/span>utilizzati (nel nostro caso quelli i cui nomi sono riportati in arancione, indicando il tempo totale di esecuzione in microsecondi. Se fossero stati eseguiti pi\u00f9 volte i blocchi di codice relativi, oltre che il tempo totale di calcolo sarebbero stati visualizzati anche il numero di esecuzioni ed il tempo medio di esecuzione di ciascun blocco.<\/p>\n<p>Infine, quando si terminer\u00e0 l&#8217;esecuzione del codice, ad esempio a distruzione della classe che conteneva le funzioni, sar\u00e0 necessario rilasciare tutti i PerformanceCounter utilizzati. L&#8217;operazione sar\u00e0 fatta utilizzando la macro <em><span style=\"color: #008000;\">$_pc_destroy_all<\/span><\/em><\/p>\n<pre class=\"lang:default decode:true\">..\r\n..\r\n\/\/ destroy all PerformanceCounter..\r\n\r\n$_pc_destroy_all\r\n\r\n..\r\n<\/pre>\n<p><span style=\"color: #ff0000;\"><img decoding=\"async\" data-attachment-id=\"3050\" data-permalink=\"https:\/\/www.colorwave.it\/blog\/colorimetria\/la-gestione-dei-dati-colorimetrici\/admin\/2018\/03\/attachment\/word-image-83\/\" data-orig-file=\"https:\/\/i0.wp.com\/www.colorwave.it\/blog\/wp-content\/uploads\/2018\/02\/word-image-60.png?fit=48%2C48&amp;ssl=1\" data-orig-size=\"48,48\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"word-image\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/i0.wp.com\/www.colorwave.it\/blog\/wp-content\/uploads\/2018\/02\/word-image-60.png?fit=48%2C48&amp;ssl=1\" data-large-file=\"https:\/\/i0.wp.com\/www.colorwave.it\/blog\/wp-content\/uploads\/2018\/02\/word-image-60.png?fit=48%2C48&amp;ssl=1\" tabindex=\"0\" role=\"button\" class=\"alignleft wp-image-3050\" src=\"https:\/\/i0.wp.com\/www.colorwave.it\/blog\/wp-content\/uploads\/2018\/02\/word-image-60.png?resize=34%2C34&#038;ssl=1\" alt=\"\" width=\"34\" height=\"34\" data-recalc-dims=\"1\" \/><\/span><span style=\"color: #333399;\"><em><strong>Attenzione<\/strong>: La macro <strong>$_pc_destroy_all<\/strong> distrugge tutti i PerformanceCounter definiti nel software e in genere non \u00e8 necessario usarla se si intende riutilizzare le istanze in pi\u00f9 fasi di esecuzione. Tutte le istanze saranno comunque rilasciate a terminazione del programma.<\/em><\/span><\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Il namespace CLKS contiene tre classi utilizzate per la gestione dei tempi di esecuzione del codice: Clock : utilizzata per valutare il trascorrere del tempo con la risoluzione massima di un secondo; \u00e8 usata per gestire processi lenti, ad esempio gli allarmi&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"aside","meta":{"_acf_changed":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"footnotes":"","_jetpack_memberships_contains_paid_content":false,"jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false},"version":2}},"categories":[114,19],"tags":[],"jetpack_publicize_connections":[],"acf":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p9rwsf-10I","jetpack_likes_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/www.colorwave.it\/blog\/wp-json\/wp\/v2\/posts\/3888"}],"collection":[{"href":"https:\/\/www.colorwave.it\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.colorwave.it\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.colorwave.it\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.colorwave.it\/blog\/wp-json\/wp\/v2\/comments?post=3888"}],"version-history":[{"count":18,"href":"https:\/\/www.colorwave.it\/blog\/wp-json\/wp\/v2\/posts\/3888\/revisions"}],"predecessor-version":[{"id":3908,"href":"https:\/\/www.colorwave.it\/blog\/wp-json\/wp\/v2\/posts\/3888\/revisions\/3908"}],"wp:attachment":[{"href":"https:\/\/www.colorwave.it\/blog\/wp-json\/wp\/v2\/media?parent=3888"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.colorwave.it\/blog\/wp-json\/wp\/v2\/categories?post=3888"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.colorwave.it\/blog\/wp-json\/wp\/v2\/tags?post=3888"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}