From 1764f50bcbdf944e003fe475aba2c26e2fd370ff Mon Sep 17 00:00:00 2001 From: Matthew Fernandez Date: Thu, 20 May 2021 20:50:11 -0700 Subject: [PATCH] compute the extent of some gvputs writes at compile time This change teaches the compiler how to unfold gvputs of a string literal into gvwrite, computing the length of the literal at compile time. This is only applied to the SVG back end in this commit. On tests/regression_tests/large/long_chain, this drops the number of gvputs calls from 2442066 to 363011, though obviously introduces the difference as gvwrite calls. The total executed instructions drops from 8093310656 to 8009099650, a speed up of ~1%. --- plugin/core/gvrender_core_svg.c | 235 ++++++++++++++++---------------- 1 file changed, 119 insertions(+), 116 deletions(-) diff --git a/plugin/core/gvrender_core_svg.c b/plugin/core/gvrender_core_svg.c index c4069e846..a5dc9a061 100644 --- a/plugin/core/gvrender_core_svg.c +++ b/plugin/core/gvrender_core_svg.c @@ -42,6 +42,9 @@ #define EDGEALIGN 0 #endif +// optimized gvputs for string literals +#define GVPUTS(job, str) gvwrite((job), (str), sizeof(str) - 1) + typedef enum { FORMAT_SVG, FORMAT_SVGZ, } format_type; /* SVG dash array */ @@ -61,7 +64,7 @@ static void svg_bzptarray(GVJ_t * job, pointf * A, int n) for (i = 0; i < n; i++) { gvwrite(job, &c, 1); gvprintdouble(job, A[i].x); - gvputs(job, ","); + GVPUTS(job, ","); gvprintdouble(job, -A[i].y); if (i == 0) c = 'C'; /* second point */ @@ -73,7 +76,7 @@ static void svg_bzptarray(GVJ_t * job, pointf * A, int n) for (i = n-1; i >= 0; i--) { gvwrite(job, &c, 1); gvprintdouble(job, A[i].x); - gvputs(job, ","); + GVPUTS(job, ","); gvprintdouble(job, -A[i].y); if (i == 0) c = 'C'; /* second point */ @@ -88,16 +91,16 @@ static void svg_print_id_class(GVJ_t * job, char* id, char* idx, char* kind, voi { char* str; - gvputs(job, "obj; - gvputs(job, " fill=\""); + GVPUTS(job, " fill=\""); if (filled == GRADIENT) { gvprintf(job, "url(#l_%d)", gid); } else if (filled == RGRADIENT) { @@ -135,12 +138,12 @@ static void svg_grstyle(GVJ_t * job, int filled, int gid) gvprintf(job, "\" fill-opacity=\"%f", ((float) obj->fillcolor.u.rgba[3] / 255.0)); } else { - gvputs(job, "none"); + GVPUTS(job, "none"); } - gvputs(job, "\" stroke=\""); + GVPUTS(job, "\" stroke=\""); svg_print_color(job, obj->pencolor); if (obj->penwidth != PENWIDTH_NORMAL) { - gvputs(job, "\" stroke-width=\""); + GVPUTS(job, "\" stroke-width=\""); gvprintdouble(job, obj->penwidth); } if (obj->pen == PEN_DASHED) { @@ -153,36 +156,36 @@ static void svg_grstyle(GVJ_t * job, int filled, int gid) gvprintf(job, "\" stroke-opacity=\"%f", ((float) obj->pencolor.u.rgba[3] / 255.0)); - gvputs(job, "\""); + GVPUTS(job, "\""); } static void svg_comment(GVJ_t * job, char *str) { - gvputs(job, "\n"); + GVPUTS(job, " -->\n"); } static void svg_begin_job(GVJ_t * job) { char *s; - gvputs(job, + GVPUTS(job, "\n"); if ((s = agget(job->gvc->g, "stylesheet")) && s[0]) { - gvputs(job, "\n"); + GVPUTS(job, "\" type=\"text/css\"?>\n"); } - gvputs(job, "\n" "\n"); } @@ -190,9 +193,9 @@ static void svg_begin_graph(GVJ_t * job) { obj_state_t *obj = job->obj; - gvputs(job, "\n", @@ -206,7 +209,7 @@ static void svg_begin_graph(GVJ_t * job) job->canvasBox.UR.x, job->canvasBox.UR.y); /* namespace of svg */ - gvputs(job, " xmlns=\"http://www.w3.org/2000/svg\"" + GVPUTS(job, " xmlns=\"http://www.w3.org/2000/svg\"" /* namespace of xlink */ " xmlns:xlink=\"http://www.w3.org/1999/xlink\"" ">\n"); @@ -214,7 +217,7 @@ static void svg_begin_graph(GVJ_t * job) static void svg_end_graph(GVJ_t * job) { - gvputs(job, "\n"); + GVPUTS(job, "\n"); } static void svg_begin_layer(GVJ_t * job, char *layername, int layerNum, @@ -226,12 +229,12 @@ static void svg_begin_layer(GVJ_t * job, char *layername, int layerNum, obj_state_t *obj = job->obj; svg_print_id_class(job, layername, NULL, "layer", obj->u.g); - gvputs(job, ">\n"); + GVPUTS(job, ">\n"); } static void svg_end_layer(GVJ_t * job) { - gvputs(job, "\n"); + GVPUTS(job, "\n"); } /* svg_begin_page: @@ -245,26 +248,26 @@ static void svg_begin_page(GVJ_t * job) /* its really just a page of the graph, but its still a graph, * and it is the entire graph if we're not currently paging */ svg_print_id_class(job, obj->id, NULL, "graph", obj->u.g); - gvputs(job, " transform=\"scale("); + GVPUTS(job, " transform=\"scale("); gvprintdouble(job, job->scale.x); - gvputs(job, " "); + GVPUTS(job, " "); gvprintdouble(job, job->scale.y); gvprintf(job, ") rotate(%d) translate(", -job->rotation); gvprintdouble(job, job->translation.x); - gvputs(job, " "); + GVPUTS(job, " "); gvprintdouble(job, -job->translation.y); - gvputs(job, ")\">\n"); + GVPUTS(job, ")\">\n"); /* default style */ if (agnameof(obj->u.g)[0] && agnameof(obj->u.g)[0] != LOCALNAMEPREFIX) { - gvputs(job, ""); + GVPUTS(job, "<title>"); gvputs(job, xml_string(agnameof(obj->u.g))); - gvputs(job, "\n"); + GVPUTS(job, "\n"); } } static void svg_end_page(GVJ_t * job) { - gvputs(job, "\n"); + GVPUTS(job, "\n"); } static void svg_begin_cluster(GVJ_t * job) @@ -272,15 +275,15 @@ static void svg_begin_cluster(GVJ_t * job) obj_state_t *obj = job->obj; svg_print_id_class(job, obj->id, NULL, "cluster", obj->u.sg); - gvputs(job, ">\n" + GVPUTS(job, ">\n" ""); gvputs(job, xml_string(agnameof(obj->u.g))); - gvputs(job, "\n"); + GVPUTS(job, "\n"); } static void svg_end_cluster(GVJ_t * job) { - gvputs(job, "\n"); + GVPUTS(job, "\n"); } static void svg_begin_node(GVJ_t * job) @@ -293,15 +296,15 @@ static void svg_begin_node(GVJ_t * job) else idx = NULL; svg_print_id_class(job, obj->id, idx, "node", obj->u.n); - gvputs(job, ">\n" + GVPUTS(job, ">\n" ""); gvputs(job, xml_string(agnameof(obj->u.n))); - gvputs(job, "\n"); + GVPUTS(job, "\n"); } static void svg_end_node(GVJ_t * job) { - gvputs(job, "\n"); + GVPUTS(job, "\n"); } static void svg_begin_edge(GVJ_t * job) @@ -310,54 +313,54 @@ static void svg_begin_edge(GVJ_t * job) char *ename; svg_print_id_class(job, obj->id, NULL, "edge", obj->u.e); - gvputs(job, ">\n" + GVPUTS(job, ">\n" ""); ename = strdup_and_subst_obj("\\E", (void *) (obj->u.e)); gvputs(job, xml_string(ename)); free(ename); - gvputs(job, "\n"); + GVPUTS(job, "\n"); } static void svg_end_edge(GVJ_t * job) { - gvputs(job, "\n"); + GVPUTS(job, "\n"); } static void svg_begin_anchor(GVJ_t * job, char *href, char *tooltip, char *target, char *id) { - gvputs(job, "" + GVPUTS(job, ">" "\n"); + GVPUTS(job, ">\n"); } static void svg_end_anchor(GVJ_t * job) { - gvputs(job, "\n" + GVPUTS(job, "\n" "\n"); } @@ -368,26 +371,26 @@ static void svg_textspan(GVJ_t * job, pointf p, textspan_t * span) char *family = NULL, *weight = NULL, *stretch = NULL, *style = NULL; unsigned int flags; - gvputs(job, "just) { case 'l': - gvputs(job, " text-anchor=\"start\""); + GVPUTS(job, " text-anchor=\"start\""); break; case 'r': - gvputs(job, " text-anchor=\"end\""); + GVPUTS(job, " text-anchor=\"end\""); break; default: case 'n': - gvputs(job, " text-anchor=\"middle\""); + GVPUTS(job, " text-anchor=\"middle\""); break; } p.y += span->yoffset_centerline; if (!obj->labeledgealigned) { - gvputs(job, " x=\""); + GVPUTS(job, " x=\""); gvprintdouble(job, p.x); - gvputs(job, "\" y=\""); + GVPUTS(job, "\" y=\""); gvprintdouble(job, -p.y); - gvputs(job, "\""); + GVPUTS(job, "\""); } pA = span->font->postscript_alias; if (pA) { @@ -414,7 +417,7 @@ static void svg_textspan(GVJ_t * job, pointf p, textspan_t * span) gvprintf(job, " font-family=\"%s", family); if (pA->svg_font_family) gvprintf(job, ",%s", pA->svg_font_family); - gvputs(job, "\""); + GVPUTS(job, "\""); if (weight) gvprintf(job, " font-weight=\"%s\"", weight); if (stretch) @@ -425,14 +428,14 @@ static void svg_textspan(GVJ_t * job, pointf p, textspan_t * span) gvprintf(job, " font-family=\"%s\"", span->font->name); if ((span->font) && (flags = span->font->flags)) { if ((flags & HTML_BF) && !weight) - gvputs(job, " font-weight=\"bold\""); + GVPUTS(job, " font-weight=\"bold\""); if ((flags & HTML_IF) && !style) - gvputs(job, " font-style=\"italic\""); + GVPUTS(job, " font-style=\"italic\""); if ((flags & (HTML_UL|HTML_S|HTML_OL))) { int comma = 0; - gvputs(job, " text-decoration=\""); + GVPUTS(job, " text-decoration=\""); if ((flags & HTML_UL)) { - gvputs(job, "underline"); + GVPUTS(job, "underline"); comma = 1; } if ((flags & HTML_OL)) { @@ -441,12 +444,12 @@ static void svg_textspan(GVJ_t * job, pointf p, textspan_t * span) } if ((flags & HTML_S)) gvprintf(job, "%sline-through", (comma?",":"")); - gvputs(job, "\""); + GVPUTS(job, "\""); } if ((flags & HTML_SUP)) - gvputs(job, " baseline-shift=\"super\""); + GVPUTS(job, " baseline-shift=\"super\""); if ((flags & HTML_SUB)) - gvputs(job, " baseline-shift=\"sub\""); + GVPUTS(job, " baseline-shift=\"sub\""); } gvprintf(job, " font-size=\"%.2f\"", span->font->size); @@ -465,17 +468,17 @@ static void svg_textspan(GVJ_t * job, pointf p, textspan_t * span) default: assert(0); /* internal error */ } - gvputs(job, ">"); + GVPUTS(job, ">"); if (obj->labeledgealigned) { gvprintf(job, "", xml_string(obj->id)); - gvputs(job, ""); + GVPUTS(job, "\">"); } gvputs(job, xml_string0(span->str, TRUE)); if (obj->labeledgealigned) - gvputs(job, ""); - gvputs(job, "\n"); + GVPUTS(job, ""); + GVPUTS(job, "\n"); } /* svg_gradstyle @@ -495,39 +498,39 @@ static int svg_gradstyle(GVJ_t * job, pointf * A, int n) gvprintf(job, "\n\n"); + GVPUTS(job, "\" >\n"); if (obj->gradient_frac > 0) gvprintf(job, "gradient_frac - 0.001); else - gvputs(job, "fillcolor); - gvputs(job, ";stop-opacity:"); + GVPUTS(job, ";stop-opacity:"); if (obj->fillcolor.type == RGBA_BYTE && obj->fillcolor.u.rgba[3] > 0 && obj->fillcolor.u.rgba[3] < 255) gvprintf(job, "%f", ((float) obj->fillcolor.u.rgba[3] / 255.0)); else - gvputs(job, "1."); - gvputs(job, ";\"/>\n"); + GVPUTS(job, "1."); + GVPUTS(job, ";\"/>\n"); if (obj->gradient_frac > 0) gvprintf(job, "gradient_frac); else - gvputs(job, "stopcolor); - gvputs(job, ";stop-opacity:"); + GVPUTS(job, ";stop-opacity:"); if (obj->stopcolor.type == RGBA_BYTE && obj->stopcolor.u.rgba[3] > 0 && obj->stopcolor.u.rgba[3] < 255) gvprintf(job, "%f", ((float) obj->stopcolor.u.rgba[3] / 255.0)); else - gvputs(job, "1."); - gvputs(job, ";\"/>\n\n\n"); + GVPUTS(job, "1."); + GVPUTS(job, ";\"/>\n\n\n"); return id; } @@ -552,24 +555,24 @@ static int svg_rgradstyle(GVJ_t * job) gvprintf(job, "\n\n", id, ifx, ify); - gvputs(job, "fillcolor); - gvputs(job, ";stop-opacity:"); + GVPUTS(job, ";stop-opacity:"); if (obj->fillcolor.type == RGBA_BYTE && obj->fillcolor.u.rgba[3] > 0 && obj->fillcolor.u.rgba[3] < 255) gvprintf(job, "%f", ((float) obj->fillcolor.u.rgba[3] / 255.0)); else - gvputs(job, "1."); - gvputs(job, ";\"/>\n" + GVPUTS(job, "1."); + GVPUTS(job, ";\"/>\n" "stopcolor); - gvputs(job, ";stop-opacity:"); + GVPUTS(job, ";stop-opacity:"); if (obj->stopcolor.type == RGBA_BYTE && obj->stopcolor.u.rgba[3] > 0 && obj->stopcolor.u.rgba[3] < 255) gvprintf(job, "%f", ((float) obj->stopcolor.u.rgba[3] / 255.0)); else - gvputs(job, "1."); - gvputs(job, ";\"/>\n\n\n"); + GVPUTS(job, "1."); + GVPUTS(job, ";\"/>\n\n\n"); return id; } @@ -584,17 +587,17 @@ static void svg_ellipse(GVJ_t * job, pointf * A, int filled) } else if (filled == (RGRADIENT)) { gid = svg_rgradstyle(job); } - gvputs(job, "\n"); + GVPUTS(job, "\"/>\n"); } static void @@ -612,16 +615,16 @@ svg_bezier(GVJ_t * job, pointf * A, int n, int arrow_at_start, } else if (filled == (RGRADIENT)) { gid = svg_rgradstyle(job); } - gvputs(job, "labeledgealigned) { - gvputs(job, " id=\""); + GVPUTS(job, " id=\""); gvputs(job, xml_string(obj->id)); - gvputs(job, "_p\" "); + GVPUTS(job, "_p\" "); } svg_grstyle(job, filled, gid); - gvputs(job, " d=\""); + GVPUTS(job, " d=\""); svg_bzptarray(job, A, n); - gvputs(job, "\"/>\n"); + GVPUTS(job, "\"/>\n"); } static void svg_polygon(GVJ_t * job, pointf * A, int n, int filled) @@ -632,36 +635,36 @@ static void svg_polygon(GVJ_t * job, pointf * A, int n, int filled) } else if (filled == (RGRADIENT)) { gid = svg_rgradstyle(job); } - gvputs(job, "\n"); + GVPUTS(job, "\"/>\n"); } static void svg_polyline(GVJ_t * job, pointf * A, int n) { int i; - gvputs(job, "\n"); + GVPUTS(job, "\"/>\n"); } /* color names from http://www.w3.org/TR/SVG/types.html */ -- 2.40.0