{"id":10614,"date":"2014-11-30T00:13:51","date_gmt":"2014-11-29T23:13:51","guid":{"rendered":"http:\/\/www.bernd-leitenberger.de\/blog\/?p=10614"},"modified":"2014-11-30T09:57:49","modified_gmt":"2014-11-30T08:57:49","slug":"der-compiler-ist-schuld-oder-auch-nicht","status":"publish","type":"post","link":"https:\/\/www.bernd-leitenberger.de\/blog\/2014\/11\/30\/der-compiler-ist-schuld-oder-auch-nicht\/","title":{"rendered":"Der Compiler ist schuld &#8211; oder auch nicht"},"content":{"rendered":"<p>Ein Compiler hat die Aufgabe eine h&ouml;here Programmiersprache in Maschinensprache zu &uuml;bersetzen. Bei Gro&szlig;rechnern haben kompilierte Programme schon in den Sechzigern die Maschinensprache sukzessive verdr&auml;ngt. Im PC Bereich war wegen der anfangs langsamen Prozessoren und dem geringen Speicher Assembler noch in den ersten Jahren wichtig, aber heute sicher nicht mehr.<\/p>\n<p>Machen wir einen geschichtlichen R&uuml;ckblick. Fr&uuml;her hat man auch evaluiert, wie effizient Compiler sind. Die Ergebnisse sind nat&uuml;rlich stark vom Quelltext abh&auml;ngig. Die NASA hat f&uuml;r die Flugsoftware der Shuttle evaluiert wie schnell HAL als h&ouml;here Programmiersprache verglichen mit Assembler war. Und das Ergebnis war damals: 10-20% langsamer. Das Betriebssystem, das zeitkritisch war, wurde daraufhin in Assembler geschrieben, die &#8222;Anwendungen&#8220; dann in HAL. Ein &auml;hnliches Ergebnis gab es bei Tests der ersten Versionen von Turbo Pascal. Das ist erfreulich, denn das Programmieren ist doch erheblich leichter in einer Hochsprache.<!--more--><\/p>\n<p>Ich glaube in den letzten Jahren habe ich kaum noch was davon gelesen das jemand in Assembler programmiert, selbst wenn man nur Inline Code verwendet, also Assembler in eine h&ouml;here Programmiersprache einbettet. Die ct&#8216; hat als SSE eingef&uuml;hrt wurde das &#8222;Apfelm&auml;nnchen&#8220; also das Fractal mal in diesen Befehlen programmiert um zu sehen wie viel schneller es ist. Ich habe mal im ct Archiv gesucht: das letzte Mal war 2008. Damals war der C-Compiler meist schneller als der eingef&uuml;gte Assembler.<\/p>\n<p>Also alles in Ordnung? Nicht unbedingt. Es gibt zwei grundlegende Probleme f&uuml;r den Compiler<\/p>\n<ul>\n<li>Er hat keinerlei Kenntnis, wie der Prozessor intern arbeitet<\/li>\n<li>Das Ausn&uuml;tzen des Befehlssatzes wird immer schwieriger, je mehr Befehle es gibt und desto spezieller sie sind<\/li>\n<li>Er muss heute Befehle erzeugen, die ihre Daten von mehreren Quelltextzeilen bekommen, also den Bereich in dem er optimiert vergr&ouml;&szlig;ern oder es wird schwieriger den richtigen Befehl ausw&auml;hlen.<\/li>\n<\/ul>\n<p>Es gibt durchaus Indizien, das hier noch einiges zu verbessern ist. Ich habe schon mal den Test der ct&#8216; der Matrixmultiplikation erw&auml;hnt. Der Benchmark ist nun extrem popelig, denn er besteht aus einer Zeile:<\/p>\n<p>c[i][j] += a[i][k] * b[k][j];<\/p>\n<p>Diese wird in drei Schleifen (i,J,k) f&uuml;r Spalten und Zeilen der beiden Matrizen durchlaufen. Nun gibt es einen Befehl, der genau diese Zeile durchf&uuml;hrt: FMA &#8211; fused Add Multiply. Dieser Befehl kann sogar vier doppelt genaue und acht einfach genaue Zahlen in einem Takt addieren und multiplizieren. Das bedeutet pro Takt sollte er acht bzw. 16 Flie&szlig;kommaoperationen durchf&uuml;hren, multipliziert man dies mit dem Takt und den acht Threads des Core i7- 4750HQ\u00a0 Prozessors so sind dies 180 GFlops bei 4 Kernen, Hyperthreading sollte weitere 30% erreichen.<\/p>\n<p>Das Ergebnis: Microsofts C-Compiler erreicht 3,2 GFlops, wenn man AVX explizit nutzen will steigert sich das auf 6,5 GFlops &#8211; er nutzt aber nur einen Kern. Mit einer Erweiterung die Autoparallisierung nutzt klettert das dann auf 37 GFLOPS. Trotzdem &#8211; nur ein Viertel der theoretischen Leistung. Es zeigte sich auch, dass die Ergebnisse extrem sensitiv waren wie der Code aufgebaut war. F&uuml;hrte man die Initialisierung von C in einer vorgezogenen Schleife durch so war die Geschwindigkeit in der Rechenschleife eine andere als wenn dies in der Rechenschleife direkt erfolgte.<\/p>\n<p>Intel als Prozessorhersteller sollte bessere Compiler liefern und sie sind auch besser. Sie kommen 5-8 GFlops bei einem Kern, auf 20 GFLOPS mit AVX Unterst&uuml;tzung und Nutzung aller Kerne, nimmt man die Erweiterung <a href=\"http:\/\/de.wikipedia.org\/wiki\/OpenMP\">OpenMP<\/a> hinzu kommt man auf 37 GFLOPS &#8211; ein vielfaches der Basisleistung. Doch programmiert man das nicht selbst sondern nutzt eine Bibliotheksfunktion, von der man annehmen kann, dass sie optimal programmiert ist, wahrscheinlich in Assembler, so erreicht man 140 GFLOPs.<\/p>\n<p>Das bedeutet man kann ein und dasselbe Programm extrem beschleunigen und das, obwohl es sich nur um eine Programmzeile handelt. Es d&uuml;rfte jedem klar sein, dass es nicht f&uuml;r jedes Problem eine Bibliotheksfunktion gibt, das Ergebnis ist also ern&uuml;chternd.<\/p>\n<p>Es gibt noch ein zweites Faktum: das waren gescheiterte Prozessoren. 1989 lancierte Intel den <a href=\"\/intels-flops.shtml\">i860 Prozessor,<\/a> genannt &#8222;Cray on a Chip&#8220;, weil er die Leistung einer <a href=\"\/cray-1.shtml\">Cray 1<\/a> erreichte. Allerdings erzeugten Compiler Programme die nur ein Achtel der theoretischen Pearkperformance erreichten. Das lag nicht nur an den Compilern, der Chip galt als schwer zu programmieren und auch Assembler erreichte nur die H&auml;lfte der Maximalleistung, aber immerhin noch viermal schneller als der Compiler.<\/p>\n<p>Das zweite war das Scheitern des Itanienporzessors. Der hatte eine Philosophie die man <a href=\"simd-vliw.shtml\">VLIW<\/a> nennt &#8211; very Long instruction Word. Dieser Ansatz soll zum einen die Abh&auml;ngigkeit vom dem Speichersystem reduzieren, dass viel langsamer als der Prozessor ist &#8211; man &uuml;bertr&auml;gt nicht einen Befehl sondern mehrere, die man zu einem Superwort zusammenfasst. Das Konzept gibt es auch bei Signalverarbeitungsprozessoren und die Cyber Star setzte es auch ein. Das zweite ist das heute alle Prozessoren nicht nur mehrere Kerne haben, sondern jeder Kern auch mehrere Recheneinheiten. W&auml;hrend die Kerne aber nach au&szlig;en hin sichtbar sind, sind es die Funktionseinheiten nicht. Die Prozessoren versuchen mit einigen Tricks diese Einheiten alle auszulasten sogar indem sie die Befehlsreihenfolge ver&auml;ndern. Der Itanium wollte diese aufwendige Logik einsparen und delegierte die Aufgabe an den Compiler. Einige Bits im Superwort enthielten die Informationen welche Befehle parallel ablaufen konnten und welche nicht. So richtig gelang dies nicht, denn die Performance war immer nur mittelm&auml;&szlig;ig. Folgerichtig stellte Intel auch diese Entwicklung ein.<\/p>\n<p>Die Chancen, dass ein Compiler optimalen Code erzeugt sind um so besser, je weniger Befehle ein Prozessor hat. Untersuchungen von Kompiliertem Code bei den damals &uuml;blichen CISC Prozessoren ergab, dass vom gesamten Befehlssatz nur ein Bruchteil sehr h&auml;ufig benutzt wurde, manche Befehle niemals. Das war damals der Startschuss f&uuml;r RISC &#8211; ein kleinerer Befehlssatz sollte schneller ausgef&uuml;hrt werden. Heute arbeiten die x86 und x64 Prozessoren intern auch mit RISC, sie machen einen Zwischenschritt und &uuml;bersetzen den CISC Code in RISC. das zeigt schon die Problematik. Trotzdem f&uuml;hrte Intel mit MMX, SSE 1-4 und AVX 1+2 immer neue Befehle ein, das mit wurde der Befehlssatz noch komplizierter und die Wahrscheinlichkeit, das der Compiler die beste L&ouml;sung findet schwindet, zumal er ja nur eine Vorschrift anwendet.<\/p>\n<p>Was ist die L&ouml;sung? Nun OpenMP zeigt eine L&ouml;sung auf. Bei OpenMP, das bei Microsofts und Intels Compiler die Performance deutlich erh&ouml;hte, gibt der Programmierer an was parelellisierbar ist. Offenbar sind diese immer noch schlauer als die Compiler. Das zeigte auch der Code denn mein Compiler erzeugte. Dort wurde ich an eine Optimierung der Core 2 Architektur erinnert. Es handelt sich um den Befehl &#8222;Speichere Daten an Adresse X&#8220; gefolgt wenige Befehle sp&auml;ter von &#8222;Lade Daten von Adresse X&#8220;. F&uuml;r einen Programmierer erscheint diese Sequenz d&auml;mlich, denn erst speichere ich einen Wert dann hole ich ihn, stattdessen kann ich ihn gleich in einem Register halten, das ist viel schneller. Obwohl nur vier der acht FPU Register genutzt wurden, fand sich genau diese Kombination im Code in dieser Zeile. Bei der Core 2 Mikroarchitektur hat man diese das Umsortieren der Befehle aufhaltende Kombination erkannt und sie blockiert nicht mehr das Umsortieren.<\/p>\n<p>Das zeigt: Compiler sind heute noch strunz doof. Der Compiler h&auml;tte eigentlich so viel wie m&ouml;glich in den Registern halten m&uuml;ssen denn der Speicherzugriff ist auch noch langsam. Was folgt daraus? Meine pers&ouml;nliche Ansicht nach sollte man das urspr&uuml;ngliche Ziel von RISC beibehalten. Anstatt das Innenleben der CPU umzumodeln, damit sie das beste aus dem Code herauskitzeln, indem sie spekulative Spr&uuml;nge vorhersagen, Register umbenennen, Befehle umsortieren oder Zugriffsmuster auf den Speicher vorhersagen sollte man wenige Befehle sehr schnell ausf&uuml;hren und das geht zum einen mit RISC zum andern mit Eingehen auf den Compiler. Beispielsweise &uuml;bergeben die meisten Compiler Parameter an Funktionen &uuml;ber den Stack &#8211; nicht nur fehlertr&auml;chtig, sondern auch langsam.<\/p>\n<p>Stattdessen sollte man f&uuml;r jede Routine eigene Register f&uuml;r die Parameter&uuml;bergabe zur Verf&uuml;gung stellen, die man als Fenster aus einem gr&ouml;&szlig;eren Registerfile ausw&auml;hlt. Dieses Konzept des Registerfiles kennen einige RISC Architekturen. Anders als beim Cache kann der Compiler so viel besser kontrollieren wo die Daten gespeichert werden. Das gilt auch f&uuml;r das Laden von Routinen. Der Compiler weri&szlig; wie gro&szlig; ein Unterprogramm ist und k&ouml;nnte es vorrausschauend in den Cache laden lassen. Auch das Konzept des VLIW ist nicht so dumm. Zumindest w&auml;re es m&ouml;glich in einem Statusbyte oder einigen Bits pro Befehl anzugeben welche Einheit man nutzt. Gleiche Werte erlauben es dem Prozessor zu erkennen: okay diese Befehle h&auml;ngen voneinander ab. Man kann mit einem Befehl eine Einheit f&uuml;r eine Befehlssequenz reservieren und wieder freigeben.<\/p>\n<p>Das sind nur einige Ideen, aber je mehr ich &uuml;ber Mikroarchitekturen schreibe, derzeit bin ich bei Nehalem, also der drittletzten Architektur die 2008n erschien, desto mehr wird mir klar, dass man eine ganze Menge Aufwand betreibt um im Prozessor die Performance zu steigern, ohne das der Anwender das heute gro&szlig; mit dem Compiler beeinflussen k&ouml;nnte. Dabei kennt der den Quelltext er kennt Zugriffsmuster und er wei&szlig; wo Routinen sind die h&auml;ufig gebraucht werden und welche wo langsam sind, bzw., welche als n&auml;chstes dran kommen (die man schon mal vom Speicher in den Cache geladen werden k&ouml;nnen). Man sollte also die Zusammenarbeit intensivieren anstatt neue CISD Befehle zu kreieren.<\/p>\n<p>Es g&auml;be sogar eine noch weitergehende M&ouml;glichkeit: die des Profilings. Das gibt es heute als Tools f&uuml;r Entwicklungsstudios, aber es wurde auch bei PC&#8217;s eingesetzt die auf dem Alpha Prozessor basierten und f&uuml;r x86 geschriebene Programme als Emulation ausf&uuml;hrten: Ein Programm wird zuerst einfach in Maschinensprache &uuml;bersetzt und vom Anwender normal benutzt. Dabei f&uuml;hrt ein mitgeliefertes Modul Buch wie oft welche Routine ausgef&uuml;hrt wird, auf welche Variablen zugegriffen wird etc. Das kann man ausbauen und auch zeitliche Zusammenh&auml;nge erkennen (eine Routine wird kurzzeitig sejr oft aufgerufen, dann wieder l&auml;ngere Zeit gar nicht). In einem zweiten Schritt wird das Programm neu &uuml;bersetzt wobei die Informationen genutzt werden es zu optimieren. Ohne spezielle SIMD Befehle z.B. Variablen in Registern dauerhaft zu halten oder zumindest im Cache, Routinen vorbeugend laden wenn man wei&szlig; das sie oft nach anderen aufgerufen werden. Man kann es aber auch nutzen um CISC Befehle wie obigen FMA zu nehmen indem man beim lauf das Zugriffsmuster und die Operationen in einem Array erkennt.<br \/>\n<img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg01.met.vgwort.de\/na\/9263e49756ed40eaa564f9df89e0fd2f\" alt=\"\" width=\"1\" height=\"1\" \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ein Compiler hat die Aufgabe eine h&ouml;here Programmiersprache in Maschinensprache zu &uuml;bersetzen. Bei Gro&szlig;rechnern haben kompilierte Programme schon in den Sechzigern die Maschinensprache sukzessive verdr&auml;ngt. Im PC Bereich war wegen der anfangs langsamen Prozessoren und dem geringen Speicher Assembler noch in den ersten Jahren wichtig, aber heute sicher nicht mehr. Machen wir einen geschichtlichen R&uuml;ckblick. [&hellip;]<\/p>\n","protected":false},"author":169,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[4],"tags":[3780,3673,3782,3781,3779],"class_list":["post-10614","post","type-post","status-publish","format-standard","hentry","category-computer","tag-avx","tag-compiler","tag-i860","tag-itanium","tag-sse","entry"],"a3_pvc":{"activated":false,"total_views":1021,"today_views":0},"jetpack-related-posts":[{"id":18676,"url":"https:\/\/www.bernd-leitenberger.de\/blog\/2026\/05\/31\/die-glorreichen-10-programmiersprachen\/","url_meta":{"origin":10614,"position":0},"title":"Die glorreichen 10 \u2013 Programmiersprachen","author":"Bernd Leitenberger","date":"31.05.2026","format":false,"excerpt":"Ich wollte mal eine Reihe in dieser Rubrik \u00fcber Programmiersprachen machen. Zuerst dachte ich daran eine Liste nach meinen pers\u00f6nlichen Favoriten zu erstellen. Anfangs bef\u00fcrchtete ich, dass ich gar nicht auf 10 komme, aber es sind tats\u00e4chlich mehr, wenngleich ich in vielen Sprachen nur kleine Programme verfasst habe oder mich\u2026","rel":"","context":"In &quot;Die Glorreichen 10&quot;","block_context":{"text":"Die Glorreichen 10","link":"https:\/\/www.bernd-leitenberger.de\/blog\/category\/allgemein\/die-glorreichen-10\/"},"img":{"alt_text":"","src":"https:\/\/vg09.met.vgwort.de\/na\/4073c4f9dc6943a08702cdde13605d43","width":350,"height":200},"classes":[]},{"id":18683,"url":"https:\/\/www.bernd-leitenberger.de\/blog\/2026\/06\/01\/die-glorreichen-10-programmiersprachen-2\/","url_meta":{"origin":10614,"position":1},"title":"Die glorreichen 10 \u2013 Programmiersprachen (2)","author":"Bernd Leitenberger","date":"01.06.2026","format":false,"excerpt":"Der heutige Teil schlie\u00dft nahtlos an den ersten Teil an, der gestern erschien. Es geht um 10 Kriterien anhand derer man Programmiersprachen kategorisieren kann. Maschinennah oder universell, aber komplex Als eine maschinennahe Sprache bezeichnet man eine Sprache, die nahe den M\u00f6glichkeiten von Prozessoren ist. Das Paradebeispiel ist C. Alle Prozessoren\u2026","rel":"","context":"In &quot;Die Glorreichen 10&quot;","block_context":{"text":"Die Glorreichen 10","link":"https:\/\/www.bernd-leitenberger.de\/blog\/category\/allgemein\/die-glorreichen-10\/"},"img":{"alt_text":"","src":"https:\/\/vg09.met.vgwort.de\/na\/7f5d9cf5265047179df05b778bf455b5","width":350,"height":200},"classes":[]},{"id":18610,"url":"https:\/\/www.bernd-leitenberger.de\/blog\/2026\/03\/27\/galileos-cds-teil-1\/","url_meta":{"origin":10614,"position":2},"title":"Galileos CDS &#8211; Teil 1","author":"Bernd Leitenberger","date":"27.03.2026","format":false,"excerpt":"Hall\u00f6chen, es wird Zeit das ich mich mal wieder melde. Es gab zwei Gr\u00fcnde, warum ich mich so rar gemacht habe. Das eine ist das es gerade nicht so viel aktuelles gibt, au\u00dfer einem Update zu Artemis, zu dem ich vielleicht noch etwas schreibe. W\u00e4hrend Trump das ganze Programm nach\u2026","rel":"","context":"In &quot;Raumfahrt&quot;","block_context":{"text":"Raumfahrt","link":"https:\/\/www.bernd-leitenberger.de\/blog\/category\/raumfahrt\/"},"img":{"alt_text":"","src":"https:\/\/vg07.met.vgwort.de\/na\/4fb81c7bafbd4d9d88b5695abdb33d29","width":350,"height":200},"classes":[]},{"id":18888,"url":"https:\/\/www.bernd-leitenberger.de\/blog\/2026\/08\/27\/die-ki-nachlese-nach-drei-jahren\/","url_meta":{"origin":10614,"position":3},"title":"Die KI &#8211; Nachlese nach drei Jahren","author":"Bernd Leitenberger","date":"27.08.2026","format":false,"excerpt":"Als die KI vor einigen Jahren aufkam, konnte man ihr noch etliche Tatsachen unterschieben. Ich empfehle, meinen Beitrag \u201eMariner\u202f10 und ChatGPT\u201c mal durchzulesen. Der Beitrag ist sehr humorvoll zu lesen, weil die KI fabuliert und sich auch entschuldigt, wenn man der KI v\u00f6lligen Unsinn als Tatsache vorsetzt. Selbst Bilder waren\u2026","rel":"","context":"In &quot;KI&quot;","block_context":{"text":"KI","link":"https:\/\/www.bernd-leitenberger.de\/blog\/category\/ki\/"},"img":{"alt_text":"","src":"\/img\/nightcafebild.jpg","width":350,"height":200,"srcset":"\/img\/nightcafebild.jpg 1x, \/img\/nightcafebild.jpg 1.5x, \/img\/nightcafebild.jpg 2x, \/img\/nightcafebild.jpg 3x"},"classes":[]},{"id":18612,"url":"https:\/\/www.bernd-leitenberger.de\/blog\/2026\/03\/28\/galileos-cds-teil-2\/","url_meta":{"origin":10614,"position":4},"title":"Galileos CDS \u2013 Teil 2","author":"Bernd Leitenberger","date":"28.03.2026","format":false,"excerpt":"So, heute geht es weiter mit Teil 2 \u00fcber Galileos CDS, dieser Beitrag schlie\u00dft nahtlos an den ersten Beitrag von gestern an, wie man schon an der ersten Textzeile sieht. Nach der Einleitung im ersten Teil geht es heute weiter damit warum der RCA 1802 genutzt wurde und was seine\u2026","rel":"","context":"In &quot;Raumfahrt&quot;","block_context":{"text":"Raumfahrt","link":"https:\/\/www.bernd-leitenberger.de\/blog\/category\/raumfahrt\/"},"img":{"alt_text":"","src":"https:\/\/vg07.met.vgwort.de\/na\/191e4b0728de42829cf656027b84dc82","width":350,"height":200},"classes":[]},{"id":18614,"url":"https:\/\/www.bernd-leitenberger.de\/blog\/2026\/03\/29\/galileos-cds-teil-3\/","url_meta":{"origin":10614,"position":5},"title":"Galileos CDS &#8211; Teil 3","author":"Bernd Leitenberger","date":"29.03.2026","format":false,"excerpt":"So nun zum dritten Teil \u00fcber das prim\u00e4re Computersystem von Galileo, das CDS. Nachdem sich die ersten beiden Teile nur mit dem RCA 1802, warum er gew\u00e4hlt wurde und seiner Architektur befassten geht es heute um das Computersystem selbst. Der Artikel schlie\u00dft so an seine beiden Vorg\u00e4nger gestern und vorgestern\u2026","rel":"","context":"In &quot;Raumfahrt&quot;","block_context":{"text":"Raumfahrt","link":"https:\/\/www.bernd-leitenberger.de\/blog\/category\/raumfahrt\/"},"img":{"alt_text":"","src":"https:\/\/vg07.met.vgwort.de\/na\/6e7f572a246b4ac395de9c260733b707","width":350,"height":200},"classes":[]}],"jetpack_sharing_enabled":true,"amp_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/www.bernd-leitenberger.de\/blog\/wp-json\/wp\/v2\/posts\/10614","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bernd-leitenberger.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bernd-leitenberger.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bernd-leitenberger.de\/blog\/wp-json\/wp\/v2\/users\/169"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bernd-leitenberger.de\/blog\/wp-json\/wp\/v2\/comments?post=10614"}],"version-history":[{"count":0,"href":"https:\/\/www.bernd-leitenberger.de\/blog\/wp-json\/wp\/v2\/posts\/10614\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.bernd-leitenberger.de\/blog\/wp-json\/wp\/v2\/media?parent=10614"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bernd-leitenberger.de\/blog\/wp-json\/wp\/v2\/categories?post=10614"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bernd-leitenberger.de\/blog\/wp-json\/wp\/v2\/tags?post=10614"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}