cout



  • Shade Of Mine schrieb:

    Wenn ich
    log<<0.78789;
    mache, dann muss das ja irgendwohin geschrieben werden. Auch wenn es gleich wieder verworfen wird, so wandelt der operator<< für double das Ding erstmal in einen string um - das kostet Zeit.

    Dem ist nicht so - wenn kein streambuf vorhanden ist (nach ostream out(0)), dann ist der Stream im Zustand 'bad'.
    Ruft man nun mit 'log<<0.78789;' den extractor für double auf, dann wird ein sentry-Objekt angelegt, welches im Konstruktor die Fehlerbits des Streams prüft, feststellt, dass da nichts geht und zurückkommt. Im Extractor wird das sentry-Objekt abgefragt, für !ok befunden, anschließend wird der Extraktor verlassen.

    Das sentry-Objekt benötigt nur genau dann (etwas) mehr Zeit, wenn der Stream ok ist. Dann holt es sich die lokale (am Stream hängende) ctype-Facette und prüft zunächst alles auf White Space Character, um diese anschließend zu überlesen, wenn welche da stehen und 'skipws' aktiv ist.
    Im Fehlerfall geschieht dies alles nicht.

    Gruß
    Werner



  • Edit: Zu langsam 🙂

    Ne, weil basic_ostream init aufruft und dort steht:

    if (_Mystrbuf == 0)
    			setstate(badbit);
    

    Und operator<<(double):

    _Myt& __CLR_OR_THIS_CALL operator<<(double _Val)
    		{	// insert a double
    		ios_base::iostate _State = ios_base::goodbit;
    		const sentry _Ok(*this);
    
    		if (_Ok)
    			{	// state okay, use facet to insert
    			const _Nput& _Nput_fac = _USE(ios_base::getloc(), _Nput);
    
    			_TRY_IO_BEGIN
    			if (_Nput_fac.put(_Iter(_Myios::rdbuf()), *this,
    				_Myios::fill(), _Val).failed())
    				_State |= ios_base::badbit;
    			_CATCH_IO_END
    			}
    
    		_Myios::setstate(_State);
    		return (*this);
    		}
    

    Die Konvertierung erfolgt erst bei put der Facette.



  • double war jetzt nur ein Beispiel...
    Dann nehmen wir halt MyDouble als Typ.



  • Und wie soll davon die Implementierung aussehen? Der nutzt doch höchstwahrscheinlich operator<< für die Attribute oder nicht?

    Oder ist das ein Typ, der ein byteweises Schreiben erfordert und daher z.B. write oder selber put nutzt? Selbst dann wird stets ein sentry-Objekt erstellt. Das macht jede Schreibfunktion auf der obersten Ebene. Und schreibt man sich eigene Funktionen, um z.B. die Performance zu erhöhen, ist man stets dazu angehalten selbst ein sentry-Objekt zu nutzen. Wenn man auch das nicht tut, dann ist man wohl selbst schuld.



  • Stell dir vor MyDouble arbeitet intern mit GMP.

    PS:
    Natuerlich kann man immer die Verantwortung an andere Uebertragen. Loest natuerlich jedes Performance Problem. Im Notfall einfach schnellere Hardware kaufen.

    PPS:
    Und spaetestens hier willst du ein Makro haben:
    log<<teureDebugFunktion();



  • Hier wird nicht ernsthaft über etwas diskutiert, was kein normaler Programmierer je machen würde?!



  • Na ja, der Verweis auf GMP hilft mir nicht, da ich es nicht kenne.

    Und wenn ein operator<< wirklich so enorm teuer ist, dann lohnt es sich in der Tat vorher zu prüfen, ob der Stream ok ist und sich entsprechende Operationen zu sparen.

    In 99% der Fälle, wenn operator<< eingesetzt wird - so würde ich behaupten - fängt sentry aber einen Schreibvorgang ab.

    Natuerlich kann man immer die Verantwortung an andere Uebertragen. Loest natuerlich jedes Performance Problem.

    Ich weiß nicht, worauf Du Dich jetzt beziehst. Viel Argumentationsgewalt sehe ich hinter deinem PS jedoch nicht.

    Edit: Ja klar kann man sich die Diskussion sparen. Aber die Aussage, dass operator<< im Falle eines fehlenden streambufs zu teuer ist, halte ich eben für nicht korrekt. Bei den Basistypen wird vom Standard Sorge getragen, dass keine Kosten entstehen. Wenn jemand einen operator<< schreibt, der dasselbe nicht tut, liegt das Problem selbstverständlich bei ihm. Dass man so keine Protokollierung an- oder ausschalten sollte (wobei mich gerade schon interessieren würde, wieso eigentlich nicht?), will ich damit ja nicht bestreiten.



  • Eisflamme schrieb:

    Dass man so keine Protokollierung an- oder ausschalten sollte (wobei mich gerade schon interessieren würde, wieso eigentlich nicht?), will ich damit ja nicht bestreiten.

    Dann ist ja OK.
    Ich hatte angst du wolltest genau das machen.



  • Aber nein, würde ich doch niemals tun, wie kannst Du so was nur von mir denken!!

    Und, was spricht jetzt dagegen? 🙂



  • Auch wenn der Overhead durch Abfragen reduziert wird, so erzeugt es trotzdem mehr Code und auch die Laufzeit wird negativ beeinflusst.

    Denn diese Abfragen werden wohlmöglich nicht mal herausoptimiert und verbleiben im Code. Rufst du nun in einer Schleife eines 3D-Spiels diese Log-Funktion auf, dann kannst du dadurch schon einige FPS verlieren.

    Eine Funktion und/oder Makro ist hier das Mittel der Wahl.



  • Da brauche ich aber ein besseres Beispiel. Wenn ich nicht gerade 10000 Mal pro Frame so eine Ausgabe mache (und dann wäre das Protokoll schnell viel zu riesig), dann ist diese Flag-Abfrage in meinen Augen vollkommen irrelevant.

    Ich meine, die normale Ausgabe, die viel mehr Aufwand ist, sollte ja das Spiel dennoch nicht so stark drosseln, dass man überhaupt nichts mehr hinbekommt. Wenn das aber der Fall ist, wird die Version mit Flag-Abfrage und sonst nichts doch unter Garantie nicht auffallen.



  • Shade Of Mine schrieb:

    Stell dir vor MyDouble arbeitet intern mit GMP.

    GMP macht das auch falsch.

    template <class T>
    inline std::ostream & operator<<
    (std::ostream &o, const __gmp_expr<T, T> &expr)
    {
      return o << expr.__get_mp(); // __get_mp gibt einen mpz_srcptr zurück
    }
    
    ostream&
    operator<< (ostream &o, mpz_srcptr z)
    {
      struct doprnt_params_t  param;
      __gmp_doprnt_params_from_ios (&param, o);
      return __gmp_doprnt_integer_ostream (o, &param,
                                           mpz_get_str (NULL, param.base, z));
    }
    
    /* The gmp_asprintf support routines never give an error, so
       __gmp_doprnt_integer shouldn't fail and it's return can just be checked
       with an ASSERT.  */
    
    ostream&
    __gmp_doprnt_integer_ostream (ostream &o, struct doprnt_params_t *p,
                                  char *s)
    {
      struct gmp_asprintf_t   d;
      char  *result;
      int   ret;
    
      /* don't show leading zeros the way printf does */
      p->prec = -1;
    
      GMP_ASPRINTF_T_INIT (d, &result);
      ret = __gmp_doprnt_integer (&__gmp_asprintf_funs_noformat, &d, p, s);
      ASSERT (ret != -1);
      __gmp_asprintf_final (&d);
      (*__gmp_free_func) (s, strlen(s)+1);
    
      gmp_allocated_string  t (result);
      return o.write (t.str, t.len);
    }
    

    sollte heissen

    template <class T>
    inline std::ostream & operator<<
    (std::ostream &o, const __gmp_expr<T, T> &expr)
    {
      return o << expr.__get_mp(); // __get_mp gibt einen mpz_srcptr zurück
    }
    
    ostream&
    operator<< (ostream &o, mpz_srcptr z)
    {
      ostream::sentry ok(o);
      if (ok) {
        struct doprnt_params_t  param;
        __gmp_doprnt_params_from_ios (&param, o);
        __gmp_doprnt_integer_ostream (o.rdbuf(), &param,
                                      mpz_get_str (NULL, param.base, z));
      }
      return o;
    }
    
    /* The gmp_asprintf support routines never give an error, so
       __gmp_doprnt_integer shouldn't fail and it's return can just be checked
       with an ASSERT.  */
    
    void
    __gmp_doprnt_integer_ostream (streambuf *sb, struct doprnt_params_t *p,
                                  char *s) noexcept
    {
      struct gmp_asprintf_t   d;
      char  *result;
      int   ret;
    
      /* don't show leading zeros the way printf does */
      p->prec = -1;
    
      GMP_ASPRINTF_T_INIT (d, &result);
      ret = __gmp_doprnt_integer (&__gmp_asprintf_funs_noformat, &d, p, s);
      ASSERT (ret != -1);
      __gmp_asprintf_final (&d);
      (*__gmp_free_func) (s, strlen(s)+1);
    
      gmp_allocated_string  t (result);
      sb->sputn (t.str, t.len); // wegen streambuf kein sentry
    }
    

    (ungetestet)

    Dann macht operator<< auch dort dort nichts wenn rdbuf()==NULL. Ausserdem ist das mindestens genauso schnell, weil ostream.write die ganzen sentry-checks sowieso macht.



  • Mich würde immer noch interessieren, wieso das Nullen des Streams keine gültige Variante ist den Logger auszuschalten. Dass das keiner so macht ist für mich kein Argument, vielleicht wusste niemand, dass es sentry gibt. Oder man wollte sich offenhalten auch Typen zu loggen, die den operator<< "falsch" überladen. Aber ansonsten finde ich das gar nicht so blöd.



  • Zu trivial? Dann reicht doch wahrscheinlich ein Satz. Ich persönlich verstehe es immer noch nicht.



  • ...



  • Okay, das klingt jetzt aber eher ideologisch als pragmatisch.


Anmelden zum Antworten