<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[[Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String]]></title><description><![CDATA[<p>Bei den multipart/* Datentypen muss man meistens Text bis zu einer Boundary lesen. Das mache mit Streams, genaugenommen mit Boost.Iostreams und einem selbstgeschriebenen Filter.</p>
<p>Vielleicht könnt ihr euch denken, dass da die Performance durchaus ein wichtiges Kriterium ist. Ich habe es schon geschafft, die Performance gegenüber der naiven Variante mehr als zu verdoppeln.</p>
<p><em><strong>Was könnte man an dem Code unten (performance-technisch) verbessern? Ideen?</strong></em></p>
<p>Übrigens benutzt der Code einen modifizierten Knuth-Morris-Pratt-Algorithmus. Es werden immer höchstens so viele Zeichen auf einmal gelesen, wie die Boundary lang ist. Die Boundary ist nicht Teil des (Ausgabe-)Streams. Wenn am Ende des Eingabestreams eine unvollständige Boundary vorkommt, wird diese auch nicht in den Ausgabestream übernommen.</p>
<pre><code class="language-cpp">// VERALTET

class boundary_filter : public boost::iostreams::multichar_input_filter {
public:
  boundary_filter(std::string const &amp;boundary)
  : boundary(boundary), buf(new char[boundary.size()]), eof(false),
    pos(boundary.size())
  {
    kmp_init();
  }

  boundary_filter(boundary_filter const &amp;o)
  : boundary(o.boundary), buf(new char[boundary.size()]), eof(false),
    pos(boundary.size())
  {
    kmp_init();
  }

private:
  struct eof_event {};

public:
  template&lt;typename Source&gt;
  std::streamsize read(Source &amp;source, char *outbuf, std::streamsize n_) {
    if (eof)
      return -1;
    if (n_ &lt;= 0)
      return 0;
    std::size_t n = std::size_t(n_);
    std::size_t i = 0;
    try {
      while (i &lt; n) {
        std::size_t c = update(source) - pos;
        if (c &gt; n - i)
          c = n - i;
        memcpy(outbuf + i, buf.get() + pos, c);
        i += c;
        pos += c;
      }
    } catch (eof_event&amp;) {
      /* snip */
      eof = true;
    }
    return i ? i : -1;
  }

private:
  template&lt;typename Source&gt;
  std::size_t update(Source &amp;source) {
    namespace io = boost::iostreams;

    bool end = false;

    std::size_t x;

    while ((x = check_boundary()) == pos)
    {
      if (end || pos == 0)
        throw eof_event();

      memmove(buf.get(), buf.get() + pos, boundary.size() - pos);

      do {
        std::streamsize c = 
          io::read(source, buf.get() + boundary.size() - pos, pos);

        if (c &lt; 0)
          break;

        pos -= c;
      } while (pos &gt; 0);

      if (pos != 0) {
        end = true;
        memmove(buf.get() + pos, buf.get(), boundary.size() - pos);
      }
    }

    return x;
  }

  void kmp_init() {
    kmp_next.resize(boundary.size() + 1);
    std::size_t i = 0;
    int j = -1;
    kmp_next[0] = -1;
    while (i &lt; boundary.size()) {
      while (j &gt;= 0 &amp;&amp; boundary[j] != boundary[i])
        j = kmp_next[j];
      ++i;
      ++j;
      kmp_next[i] = j;
    }
  }

  std::size_t check_boundary() {
    std::size_t i = pos;
    int j = 0;
    std::size_t a = i;
    while (i &lt; boundary.size()) {
      while (j &gt;= 0 &amp;&amp; buf[i] != boundary[j])
        j = kmp_next[j];
      ++i;
      ++j;
      if (j &lt;= 0)
        a = i;
    }
    return a;
  }

  /* snip */

private:
  std::string boundary;
  boost::scoped_array&lt;char&gt; buf;
  bool eof;
  std::size_t pos;
  std::vector&lt;int&gt; kmp_next;
};
</code></pre>
<p>Meine Vermutung ist ja, dass die beiden memmove noch durch etwas schnelleres ersetzt werden könnten, nur weiß ich nicht, durch was. Vielleicht gäbe es aber noch an anderen Stellen Potenzial.</p>
<p>Danke auf jeden Fall und viel Spaß.</p>
<p>PS: Ja, ich bin auf meine Variablen-Benenn-Künste auch sehr stolz. &quot;a&quot; und &quot;c&quot; und &quot;x&quot; zeigen ja, wie kreativ ich da bin. <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f921.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--clown_face"
      title=":clown:"
      alt="🤡"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/topic/193199/erledigt-wie-könnte-diese-klasse-noch-schneller-werden-streaming-bis-zu-einem-boundary-string</link><generator>RSS for Node</generator><lastBuildDate>Tue, 30 Jun 2026 16:11:46 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/193199.rss" rel="self" type="application/rss+xml"/><pubDate>Sun, 23 Sep 2007 18:32:40 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Tue, 25 Sep 2007 15:31:22 GMT]]></title><description><![CDATA[<p>Bei den multipart/* Datentypen muss man meistens Text bis zu einer Boundary lesen. Das mache mit Streams, genaugenommen mit Boost.Iostreams und einem selbstgeschriebenen Filter.</p>
<p>Vielleicht könnt ihr euch denken, dass da die Performance durchaus ein wichtiges Kriterium ist. Ich habe es schon geschafft, die Performance gegenüber der naiven Variante mehr als zu verdoppeln.</p>
<p><em><strong>Was könnte man an dem Code unten (performance-technisch) verbessern? Ideen?</strong></em></p>
<p>Übrigens benutzt der Code einen modifizierten Knuth-Morris-Pratt-Algorithmus. Es werden immer höchstens so viele Zeichen auf einmal gelesen, wie die Boundary lang ist. Die Boundary ist nicht Teil des (Ausgabe-)Streams. Wenn am Ende des Eingabestreams eine unvollständige Boundary vorkommt, wird diese auch nicht in den Ausgabestream übernommen.</p>
<pre><code class="language-cpp">// VERALTET

class boundary_filter : public boost::iostreams::multichar_input_filter {
public:
  boundary_filter(std::string const &amp;boundary)
  : boundary(boundary), buf(new char[boundary.size()]), eof(false),
    pos(boundary.size())
  {
    kmp_init();
  }

  boundary_filter(boundary_filter const &amp;o)
  : boundary(o.boundary), buf(new char[boundary.size()]), eof(false),
    pos(boundary.size())
  {
    kmp_init();
  }

private:
  struct eof_event {};

public:
  template&lt;typename Source&gt;
  std::streamsize read(Source &amp;source, char *outbuf, std::streamsize n_) {
    if (eof)
      return -1;
    if (n_ &lt;= 0)
      return 0;
    std::size_t n = std::size_t(n_);
    std::size_t i = 0;
    try {
      while (i &lt; n) {
        std::size_t c = update(source) - pos;
        if (c &gt; n - i)
          c = n - i;
        memcpy(outbuf + i, buf.get() + pos, c);
        i += c;
        pos += c;
      }
    } catch (eof_event&amp;) {
      /* snip */
      eof = true;
    }
    return i ? i : -1;
  }

private:
  template&lt;typename Source&gt;
  std::size_t update(Source &amp;source) {
    namespace io = boost::iostreams;

    bool end = false;

    std::size_t x;

    while ((x = check_boundary()) == pos)
    {
      if (end || pos == 0)
        throw eof_event();

      memmove(buf.get(), buf.get() + pos, boundary.size() - pos);

      do {
        std::streamsize c = 
          io::read(source, buf.get() + boundary.size() - pos, pos);

        if (c &lt; 0)
          break;

        pos -= c;
      } while (pos &gt; 0);

      if (pos != 0) {
        end = true;
        memmove(buf.get() + pos, buf.get(), boundary.size() - pos);
      }
    }

    return x;
  }

  void kmp_init() {
    kmp_next.resize(boundary.size() + 1);
    std::size_t i = 0;
    int j = -1;
    kmp_next[0] = -1;
    while (i &lt; boundary.size()) {
      while (j &gt;= 0 &amp;&amp; boundary[j] != boundary[i])
        j = kmp_next[j];
      ++i;
      ++j;
      kmp_next[i] = j;
    }
  }

  std::size_t check_boundary() {
    std::size_t i = pos;
    int j = 0;
    std::size_t a = i;
    while (i &lt; boundary.size()) {
      while (j &gt;= 0 &amp;&amp; buf[i] != boundary[j])
        j = kmp_next[j];
      ++i;
      ++j;
      if (j &lt;= 0)
        a = i;
    }
    return a;
  }

  /* snip */

private:
  std::string boundary;
  boost::scoped_array&lt;char&gt; buf;
  bool eof;
  std::size_t pos;
  std::vector&lt;int&gt; kmp_next;
};
</code></pre>
<p>Meine Vermutung ist ja, dass die beiden memmove noch durch etwas schnelleres ersetzt werden könnten, nur weiß ich nicht, durch was. Vielleicht gäbe es aber noch an anderen Stellen Potenzial.</p>
<p>Danke auf jeden Fall und viel Spaß.</p>
<p>PS: Ja, ich bin auf meine Variablen-Benenn-Künste auch sehr stolz. &quot;a&quot; und &quot;c&quot; und &quot;x&quot; zeigen ja, wie kreativ ich da bin. <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f921.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--clown_face"
      title=":clown:"
      alt="🤡"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/post/1371169</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1371169</guid><dc:creator><![CDATA[Mr. N]]></dc:creator><pubDate>Tue, 25 Sep 2007 15:31:22 GMT</pubDate></item><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Sun, 23 Sep 2007 19:47:38 GMT]]></title><description><![CDATA[<p>Mr. N schrieb:</p>
<blockquote>
<p>Meine Vermutung ist ja, dass die beiden memmove noch durch etwas schnelleres ersetzt werden könnten, nur weiß ich nicht, durch was. Vielleicht gäbe es aber noch an anderen Stellen Potenzial.</p>
</blockquote>
<p>wie waere es mit einem ganz primitiven ansatz wie z.b. erstmal profilen um rauszufinden welche stellen die kritischen sind? dann wuerdest du auch all das aliasing und die hazards sehen. <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f62e.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--face_with_open_mouth"
      title=":open_mouth:"
      alt="😮"
    /><br />
Ins blaue optimieren ist natuerlich lustig, aber wenig gewinnbringend.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1371239</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1371239</guid><dc:creator><![CDATA[rapso]]></dc:creator><pubDate>Sun, 23 Sep 2007 19:47:38 GMT</pubDate></item><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Sun, 23 Sep 2007 19:54:59 GMT]]></title><description><![CDATA[<p>profiler würde ich auch vorschlagen</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1371244</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1371244</guid><dc:creator><![CDATA[jap]]></dc:creator><pubDate>Sun, 23 Sep 2007 19:54:59 GMT</pubDate></item><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Sun, 23 Sep 2007 21:35:03 GMT]]></title><description><![CDATA[<p>Wie soll ich das profilen, wenn der alle Funktionen inlinet? <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f603.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--grinning_face_with_big_eyes"
      title=":D"
      alt="😃"
    /> Also ich habe schon versucht, das mit gprof zu profilen, aber das war irgendwie nicht sehr aussagekräftig. Soll ich etwa mit -O0 profilen? Das wäre doch genauso witzlos?</p>
<p>Außerdem hab ich ja einen Benchmark, mit dem ich prüfen kann, wie viel schneller es geworden ist.</p>
<p>Danke trotzdem.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1371269</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1371269</guid><dc:creator><![CDATA[Mr. N]]></dc:creator><pubDate>Sun, 23 Sep 2007 21:35:03 GMT</pubDate></item><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Sun, 23 Sep 2007 21:36:42 GMT]]></title><description><![CDATA[<p><a class="plugin-mentions-user plugin-mentions-a" href="https://www.c-plusplus.net/forum/uid/1029">@rapso</a>: Was meinst du eigentlich mit Aliasing und Hazards?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1371270</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1371270</guid><dc:creator><![CDATA[Mr. N]]></dc:creator><pubDate>Sun, 23 Sep 2007 21:36:42 GMT</pubDate></item><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Sun, 23 Sep 2007 22:39:38 GMT]]></title><description><![CDATA[<p>Nachdem ich zunächst falsch gemessen habe, stelle ich fest, dass das Verwenden eines Doppelpuffers und memcpy statt memmove keine signifikante Performance-Verbesserung bringt.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1371276</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1371276</guid><dc:creator><![CDATA[Mr. N]]></dc:creator><pubDate>Sun, 23 Sep 2007 22:39:38 GMT</pubDate></item><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Tue, 25 Sep 2007 02:45:25 GMT]]></title><description><![CDATA[<p>__restrict bringt einiges</p>
<pre><code class="language-cpp">//VERALTET
class boundary_filter : public boost::iostreams::multichar_input_filter {
public:
  boundary_filter(std::string const &amp;boundary)
  : boundary(boundary),
    buf(new char[boundary.size()]),
    eof(false),
    pos(boundary.size())
  {
    kmp_init();
  }

  boundary_filter(boundary_filter const &amp;o)
  : boundary(o.boundary),
    buf(new char[boundary.size()]),
    eof(false),
    pos(boundary.size())
  {
    kmp_init();
  }

  ~boundary_filter() {
    delete [] buf;
  }

private:
  struct eof_event {};

public:
  template&lt;typename Source&gt;
  std::streamsize read(
      Source &amp; __restrict source,
      char * __restrict outbuf,
      std::streamsize n_)
  {
    if (eof)
      return -1;
    if (n_ &lt;= 0)
      return 0;
    std::size_t n = std::size_t(n_);
    std::size_t i = 0;
    try {
      while (i &lt; n) {
        std::size_t c = update(source) - pos;
        if (c &gt; n - i)
          c = n - i;
        memcpy(outbuf + i, buf + pos, c);
        i += c;
        pos += c;
      }
    } catch (eof_event&amp;) {
      /* snip */
      eof = true;
    }
    return i ? i : -1;
  }

private:
  template&lt;typename Source&gt;
  std::size_t update(Source &amp; __restrict source) {
    namespace io = boost::iostreams;

    bool end = false;

    std::size_t x;

    while ((x = check_boundary()) == pos)
    {
      if (end || pos == 0)
        throw eof_event();

      memmove(buf, buf + pos, boundary.size() - pos);

      do {
        std::streamsize c = 
          io::read(source, buf + boundary.size() - pos, pos);

        if (c &lt; 0)
          break;

        pos -= c;
      } while (pos &gt; 0);

      if (pos != 0) {
        end = true;
        memmove(buf + pos, buf, boundary.size() - pos);
      }
    }

    return x;
  }

  void kmp_init() {
    kmp_next.resize(boundary.size() + 1);
    std::size_t i = 0;
    int j = -1;
    kmp_next[0] = -1;
    while (i &lt; boundary.size()) {
      while (j &gt;= 0 &amp;&amp; boundary[j] != boundary[i])
        j = kmp_next[j];
      ++i;
      ++j;
      kmp_next[i] = j;
    }
  }

  std::size_t check_boundary() {
    std::size_t i = pos;
    int j = 0;
    std::size_t a = i;
    while (i &lt; boundary.size()) {
      while (j &gt;= 0 &amp;&amp; buf[i] != boundary[j])
        j = kmp_next[j];
      ++i;
      ++j;
      if (j &lt;= 0)
        a = i;
    }
    return a;
  }

  /* snip */

private:
  std::string const boundary;
  char * __restrict buf;
  bool eof;
  std::size_t pos;
  std::vector&lt;int&gt; kmp_next;
};
</code></pre>
]]></description><link>https://www.c-plusplus.net/forum/post/1371283</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1371283</guid><dc:creator><![CDATA[Mr. N]]></dc:creator><pubDate>Tue, 25 Sep 2007 02:45:25 GMT</pubDate></item><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Mon, 24 Sep 2007 05:08:33 GMT]]></title><description><![CDATA[<p>Mr. N schrieb:</p>
<blockquote>
<p>__restrict bringt einiges</p>
</blockquote>
<p>als lustige keywords gibt's noch const, inline (bzw force inline), register, asm etc. wenn du die in kombinationen ausprobierst, bekommst du sicher konstelationen mit denen es ebenfalls schneller wird.</p>
<p>und ja, alternativ fuehrt man profiling auf einem optimierten build aus. <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f644.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--face_with_rolling_eyes"
      title=":rolling_eyes:"
      alt="🙄"
    /></p>
]]></description><link>https://www.c-plusplus.net/forum/post/1371302</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1371302</guid><dc:creator><![CDATA[rapso]]></dc:creator><pubDate>Mon, 24 Sep 2007 05:08:33 GMT</pubDate></item><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Mon, 24 Sep 2007 06:47:38 GMT]]></title><description><![CDATA[<p>buf muss nicht unbedingt auf die Größe von boundary beschränkt sein - so lässt sich die Anzahl der Aufrufe von memcpy (dass bei kurzen Blöcken ohnehin für gewöhnlich nicht optimal ist) verringern. Und der Algorithmus selbst ist nicht unbedingt die beste Wahl - aber das ist auch eine Frage des konkreten Inputs.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1371323</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1371323</guid><dc:creator><![CDATA[camper]]></dc:creator><pubDate>Mon, 24 Sep 2007 06:47:38 GMT</pubDate></item><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Mon, 24 Sep 2007 13:22:46 GMT]]></title><description><![CDATA[<p>rapso schrieb:</p>
<blockquote>
<p>Mr. N schrieb:</p>
<blockquote>
<p>__restrict bringt einiges</p>
</blockquote>
<p>als lustige keywords gibt's noch const, inline (bzw force inline), register, asm etc. wenn du die in kombinationen ausprobierst, bekommst du sicher konstelationen mit denen es ebenfalls schneller wird.</p>
<p>und ja, alternativ fuehrt man profiling auf einem optimierten build aus. <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f644.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--face_with_rolling_eyes"
      title=":rolling_eyes:"
      alt="🙄"
    /></p>
</blockquote>
<p>Ich habe bereits Profiling versucht - mit unterschiedlichen optimierten Builds! <img
      src="https://www.c-plusplus.net/forum/plugins/nodebb-plugin-emoji/emoji/emoji-one/1f621.png?v=ab1pehoraso"
      class="not-responsive emoji emoji-emoji-one emoji--pouting_face"
      title=":rage:"
      alt="😡"
    /></p>
<p>Die Zahlen waren nur nichtssagend, weil<br />
(a) wenn ich Inlining ausschalte, wird das Ding um Faktor 20 langsamer - und dass die Relationen gleich bleiben halte ich für extrem unwahrscheinlich<br />
(b) mit Inlining ist das ganze nur ein großer Funktionsblob.</p>
<p>Mag sein, dass ich nicht richtig profilen kann, aber ich habs versucht.</p>
<p>Mir wildes zufälliges Optimieren vorzuwerfen ist auch eher Blödsinn: Ich weiß sehr genau, was __restrict bewirkt. Selbst wenn ich allerdings zufällig Optimieren würde, wäre das kein Scham, schließlich habe ich einen Benchmark.</p>
<p>Ich weiß übrigens sehr genau, dass register, inline, const, etc. eher wenig bringen werden.</p>
<p>camper schrieb:</p>
<blockquote>
<p>buf muss nicht unbedingt auf die Größe von boundary beschränkt sein - so lässt sich die Anzahl der Aufrufe von memcpy (dass bei kurzen Blöcken ohnehin für gewöhnlich nicht optimal ist) verringern. Und der Algorithmus selbst ist nicht unbedingt die beste Wahl - aber das ist auch eine Frage des konkreten Inputs.</p>
</blockquote>
<p>Also das memcpy lässt sich nicht vermeiden, schließlich ist outbuf von außen vorgegeben. Und eine for-Schleife ist auch bei den relativ kurzen Mustern von vielleicht 40 Zeichen langsamer als memcpy.</p>
<p>Wenn du die memmove meinst, ich werde mal darüber nachdenken, welche Größen für buf denn Sinn machen würden.</p>
<p>Was meinst du mit &quot;der Algorithmus ist nicht optimal&quot;? Welcher wäre denn besser? Also Boyer-Moore zum Beispiel würde AFAIK nicht gehen, weil der rückwärts matcht und das wäre hier total falsch.</p>
<p>Update: Ich werde nun mal versuchen, was für Ergebnisse mir AMD CodeAnalyst ausspuckt (hab ja schließlich einen AMD Prozessor), bzw. erstmal lad ich das Ding runter.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1371610</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1371610</guid><dc:creator><![CDATA[Mr. N]]></dc:creator><pubDate>Mon, 24 Sep 2007 13:22:46 GMT</pubDate></item><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Mon, 24 Sep 2007 15:20:04 GMT]]></title><description><![CDATA[<p>CodeAnalyst hab ich nicht zum Laufen gebracht, aber oprofile bringt bessere Ergebnisse als gprof:</p>
<pre><code class="language-cpp">CPU: AMD64 processors, speed 2000 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Cycles outside of halt state) with a unit mask of 0x00 (No unit mask) count 100000
samples  %        symbol name
72637    49.6809  boost::iostreams::detail::indirect_streambuf&lt;rest::utils::boundary_filter, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt;, boost::iostreams::
input&gt;::underflow()
64166    43.8871  memcpy
3488      2.3857  std::basic_streambuf&lt;char, std::char_traits&lt;char&gt; &gt;::xsgetn(char*, long)
3202      2.1900  memmove
...
</code></pre>
<p>Also, ich denke, das memmove ist kein relevantes Performance-Problem, dementsprechend würden auch größere Puffer nichts helfen.</p>
<p>Mal sehen, ob ich irgendwie noch Ergebnisse mit besserer Granularität erzeugen kann.</p>
<p>Update:</p>
<pre><code class="language-cpp">samples  %        symbol name
548120   53.3285  (anonymous namespace)::testcase2()
--&gt; 363464   35.3627  rest::utils::boundary_filter::check_boundary()
22630     2.2018  main
--&gt; 20907     2.0341  unsigned long rest::utils::boundary_filter::update&lt;boost::iostreams::detail::linked_streambuf&lt;char, std::char_traits&lt;char&gt; &gt; &gt;(boost::iostr
eams::detail::linked_streambuf&lt;char, std::char_traits&lt;char&gt; &gt;&amp;)
17805     1.7323  _fini
14270     1.3884  long rest::utils::boundary_filter::read&lt;boost::iostreams::detail::linked_streambuf&lt;char, std::char_traits&lt;char&gt; &gt; &gt;(boost::iostreams::detai
l::linked_streambuf&lt;char, std::char_traits&lt;char&gt; &gt;&amp;, char*, long)
...
</code></pre>
<p>(Komisch, memcpy und memmove kommen nichtmal mehr vor.)</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1371697</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1371697</guid><dc:creator><![CDATA[Mr. N]]></dc:creator><pubDate>Mon, 24 Sep 2007 15:20:04 GMT</pubDate></item><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Mon, 24 Sep 2007 16:34:41 GMT]]></title><description><![CDATA[<p>Ich schätze mal memmove wird intern memcpy aufrufen (z.b. wenn nicht rückwärts kopiert werden muss) - bist du sicher dass die 43% memcpy() nicht einfach daher kommen?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1371775</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1371775</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Mon, 24 Sep 2007 16:34:41 GMT</pubDate></item><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Mon, 24 Sep 2007 17:09:53 GMT]]></title><description><![CDATA[<p>hustbaer schrieb:</p>
<blockquote>
<p>Ich schätze mal memmove wird intern memcpy aufrufen (z.b. wenn nicht rückwärts kopiert werden muss) - bist du sicher dass die 43% memcpy() nicht einfach daher kommen?</p>
</blockquote>
<p>Hmm kann sein, ich schau mir mal die glibc-Implementierung an.</p>
<p>Ich denke aber, ich sollte erstmal schauen, ob der modifizierte KMP optimal ist. Jemand ne Idee, welcher Algorithmus schneller sein könnte?</p>
<p>EDIT: memmove verwendet memcpy nicht. Beide verwenden aber hochinteressante Makros, die ich mir mal anschauen werde.<br />
EDIT 2: Die Makros würden mir zwar ein paar innere ifs einsparen, leider sind die aber nicht öffentlich.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1371796</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1371796</guid><dc:creator><![CDATA[Mr. N]]></dc:creator><pubDate>Mon, 24 Sep 2007 17:09:53 GMT</pubDate></item><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Mon, 24 Sep 2007 21:45:53 GMT]]></title><description><![CDATA[<p>Ich habe mal versucht, die Lesbarkeit der Klasse zu erhöhen:</p>
<pre><code class="language-cpp">class boundary_filter : public boost::iostreams::multichar_input_filter {
public:
  boundary_filter(std::string const &amp;boundary)
  : boundary(boundary),
    buf(new char[boundary.size()]),
    eof(false),
    pos(boundary.size())
  {
    kmp_init();
  }

  boundary_filter(boundary_filter const &amp;o)
  : boundary(o.boundary),
    buf(new char[boundary.size()]),
    eof(false),
    pos(boundary.size())
  {
    kmp_init();
  }

  ~boundary_filter() {
    delete [] buf;
  }

private:
  struct eof_event {};

public:
  template&lt;typename Source&gt;
  std::streamsize read(Source &amp; __restrict, char * __restrict, std::streamsize);

private:
  template&lt;typename Source&gt;
  std::size_t update(Source &amp; __restrict);

  void kmp_init();
  std::size_t check_boundary();

  template&lt;typename Source&gt;
  void skip_transport_padding(Source &amp; __restrict);

private:
  std::string const boundary;
  char * __restrict buf;
  bool eof;
  std::size_t pos;
  std::vector&lt;int&gt; kmp_next;
};
BOOST_IOSTREAMS_PIPABLE(boundary_filter, 0)

template&lt;typename Source&gt;
std::streamsize boundary_filter::read(
    Source &amp; __restrict source,
    char * __restrict outbuf,
    std::streamsize outbuf_size_)
{
  if (eof)
    return -1;
  if (outbuf_size_ &lt;= 0)
    return 0;
  std::size_t outbuf_size = std::size_t(outbuf_size_);
  std::size_t outbuf_pos = 0;
  try {
    while (outbuf_pos &lt; outbuf_size) {
      std::size_t fresh_bytes = update(source) - pos;
      std::size_t read_bytes = std::min(fresh_bytes, outbuf_size - outbuf_pos);
      memcpy(outbuf + outbuf_pos, buf + pos, read_bytes);
      outbuf_pos += read_bytes;
      pos += read_bytes;
    }
  } catch (eof_event&amp;) {
    skip_transport_padding(source);
    eof = true;
  }
  return outbuf_pos ? outbuf_pos : -1;
}

template&lt;typename Source&gt;
std::size_t boundary_filter::update(Source &amp; __restrict source) {
  namespace io = boost::iostreams;

  bool end_of_input = false;
  std::size_t boundary_pos;

  while ((boundary_pos = check_boundary()) == pos) {
    if (end_of_input || pos == 0)
      throw eof_event();

    memmove(buf, buf + pos, boundary.size() - pos);

    do {
      std::streamsize input_size =
        io::read(source, buf + boundary.size() - pos, pos);

      if (input_size &lt; 0)
        break;

      pos -= input_size;
    } while (pos &gt; 0);

    if (pos != 0) {
      end_of_input = true;
      memmove(buf + pos, buf, boundary.size() - pos);
    }
  }

  return boundary_pos;
}

inline void boundary_filter::kmp_init() {
  kmp_next.resize(boundary.size() + 1);
  std::size_t i = 0;
  int j = -1;
  kmp_next[0] = -1;
  while (i &lt; boundary.size()) {
    while (j &gt;= 0 &amp;&amp; boundary[j] != boundary[i])
      j = kmp_next[j];
    ++i;
    ++j;
    kmp_next[i] = j;
  }
}

inline std::size_t boundary_filter::check_boundary() {
  std::size_t i = pos;
  int j = 0;
  std::size_t x = i;
  while (i &lt; boundary.size()) {
    while (j &gt;= 0 &amp;&amp; buf[i] != boundary[j])
      j = kmp_next[j];
    ++i;
    ++j;
    if (j &lt;= 0)
      x = i;
  }
  return x;
}
</code></pre>
]]></description><link>https://www.c-plusplus.net/forum/post/1372013</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1372013</guid><dc:creator><![CDATA[Mr. N]]></dc:creator><pubDate>Mon, 24 Sep 2007 21:45:53 GMT</pubDate></item><item><title><![CDATA[Reply to [Erledigt] Wie könnte diese Klasse noch schneller werden: Streaming bis zu einem Boundary-String on Tue, 25 Sep 2007 14:50:16 GMT]]></title><description><![CDATA[<p>Das Optimierungsprojekt ist nun abgeschlossen. Vielen Dank an alle, die geholfen haben.</p>
<p>Das Ergebnis kann sich sehen lassen: Der Slowdown-Faktor konnte von 7-9 auf 2.8-3.2 gesenkt werden. (Der Faktor misst den Unterschied zwischen einem ungefilterten und einem gefilterten Stream. Ein Teil des Faktors entsteht allein schon dadurch, dass überhaupt ein Filter existiert.)</p>
<p>Ich denke, mit der jetzigen Geschwindigkeit kann man leben, zumal die Flexibilität des Filters nicht geopfert werden musste.</p>
<p>Also, Danke.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/1372472</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/1372472</guid><dc:creator><![CDATA[Mr. N]]></dc:creator><pubDate>Tue, 25 Sep 2007 14:50:16 GMT</pubDate></item></channel></rss>