does virtualizing all methods slow C++ down ?

Discussion in 'C++' started by Lynn McGuire, Sep 6, 2011.

  1. Lynn McGuire

    Lynn McGuire Guest

    Does virtualizing all methods slow C++ down ? We
    virtualize all methods as a matter of course since
    we use groups of objects all over the place. To
    illustrate this I have included the top portion of
    our header files:

    class GenGroup : public DataGroup
    {
    public:
    int transferingDataNow;
    public:
    // constructor
    GenGroup ();
    GenGroup (const GenGroup & rhs);
    GenGroup & operator = (const GenGroup & rhs);
    // destructor
    virtual ~GenGroup ();
    virtual GenGroup * clone () { return new GenGroup ( * this); }
    virtual void makeUnitsInput (InputCollection * anInpCol);
    virtual void reinitStreamBox (FmFile * FMFile);
    virtual DataDescriptor * descriptor (int aSymbol, int version);
    ....

    Thanks
    Lynn
     
    Lynn McGuire, Sep 6, 2011
    #1
    1. Advertising

  2. Lynn McGuire

    Christopher Guest

    On Sep 6, 11:31 am, Lynn McGuire <> wrote:
    > Does virtualizing all methods slow C++ down ?  We
    > virtualize all methods as a matter of course since
    > we use groups of objects all over the place.  To
    > illustrate this I have included the top portion of
    > our header files:
    >
    > class GenGroup : public DataGroup
    > {
    > public:
    >     int transferingDataNow;
    > public:
    >        //  constructor
    >     GenGroup ();
    >     GenGroup (const GenGroup & rhs);
    >     GenGroup & operator = (const GenGroup & rhs);
    >        //  destructor
    >     virtual ~GenGroup ();
    >     virtual GenGroup * clone () { return new GenGroup ( * this); }
    >     virtual void makeUnitsInput (InputCollection * anInpCol);
    >     virtual void reinitStreamBox (FmFile * FMFile);
    >     virtual DataDescriptor * descriptor (int aSymbol, int version);
    > ...
    >
    > Thanks
    > Lynn


    If you virtualize one, there is no cost for virtualizing the others.
    There is a cost associated with virtualizing one method. See the FAQ.

    I wouldn't virtualize every single method in every single class
    though. That is just not thoughtful design. When I see a virtual
    method, I assume the author INTENDED for the class to be derived from.
    Surely, you have a concrete class somewhere.
     
    Christopher, Sep 6, 2011
    #2
    1. Advertising

  3. Lynn McGuire <> writes:

    > Does virtualizing all methods slow C++ down ? We
    > virtualize all methods as a matter of course since
    > we use groups of objects all over the place.


    Not sure I understand what you mean, but yes, virtualizing all
    (non-really-virtual) methods will slow things down, in cases where the
    compiler cannot statically decide the class where the method is defined.
    In the worst case:

    - all calls through a reference or pointer will be indirect calls
    - all other calls will be direct calls.

    The branch predictor may help here, but you still pay some price. In
    your example:

    > virtual GenGroup * clone () { return new GenGroup ( * this); }


    the compiler would normally (probably) inline any call to this method in
    the non-virtual case. With virtual, all calls to this method through a
    ref/pointer will be translated into an indirect call.

    (I know, cloning usually requires dynamic binding, I used this example
    because it was the only method potentially inlinable.)

    C++11 provides the "final" qualifier, which may help here. Whether it is
    taken into account depends on your compiler.

    -- Alain.
     
    Alain Ketterlin, Sep 6, 2011
    #3
  4. Lynn McGuire

    Lynn McGuire Guest

    On 9/6/2011 12:00 PM, Christopher wrote:
    > On Sep 6, 11:31 am, Lynn McGuire<> wrote:
    >> Does virtualizing all methods slow C++ down ? We
    >> virtualize all methods as a matter of course since
    >> we use groups of objects all over the place. To
    >> illustrate this I have included the top portion of
    >> our header files:
    >>
    >> class GenGroup : public DataGroup
    >> {
    >> public:
    >> int transferingDataNow;
    >> public:
    >> // constructor
    >> GenGroup ();
    >> GenGroup (const GenGroup& rhs);
    >> GenGroup& operator = (const GenGroup& rhs);
    >> // destructor
    >> virtual ~GenGroup ();
    >> virtual GenGroup * clone () { return new GenGroup ( * this); }
    >> virtual void makeUnitsInput (InputCollection * anInpCol);
    >> virtual void reinitStreamBox (FmFile * FMFile);
    >> virtual DataDescriptor * descriptor (int aSymbol, int version);
    >> ...
    >>
    >> Thanks
    >> Lynn

    >
    > If you virtualize one, there is no cost for virtualizing the others.
    > There is a cost associated with virtualizing one method. See the FAQ.
    >
    > I wouldn't virtualize every single method in every single class
    > though. That is just not thoughtful design. When I see a virtual
    > method, I assume the author INTENDED for the class to be derived from.
    > Surely, you have a concrete class somewhere.


    We have so many methods in our 600 classes and 700K lines
    of code that we are not sure what needs to be virtual and
    does not. So we do all to keep from missing 1, 2 or 20.

    Thanks,
    Lynn
     
    Lynn McGuire, Sep 6, 2011
    #4
  5. Lynn McGuire

    Lynn McGuire Guest

    On 9/6/2011 12:12 PM, Alain Ketterlin wrote:
    > Lynn McGuire<> writes:
    >
    >> Does virtualizing all methods slow C++ down ? We
    >> virtualize all methods as a matter of course since
    >> we use groups of objects all over the place.

    >
    > Not sure I understand what you mean, but yes, virtualizing all
    > (non-really-virtual) methods will slow things down, in cases where the
    > compiler cannot statically decide the class where the method is defined.
    > In the worst case:
    >
    > - all calls through a reference or pointer will be indirect calls
    > - all other calls will be direct calls.
    >
    > The branch predictor may help here, but you still pay some price. In
    > your example:
    >
    >> virtual GenGroup * clone () { return new GenGroup ( * this); }

    >
    > the compiler would normally (probably) inline any call to this method in
    > the non-virtual case. With virtual, all calls to this method through a
    > ref/pointer will be translated into an indirect call.
    >
    > (I know, cloning usually requires dynamic binding, I used this example
    > because it was the only method potentially inlinable.)
    >
    > C++11 provides the "final" qualifier, which may help here. Whether it is
    > taken into account depends on your compiler.
    >
    > -- Alain.


    Thanks, I had not thought about the direct calls just
    directly binding to the proper method. I'll bet that
    there are few direct calls in our code though as we
    have many children classes.

    ObjPtr -> DataGroup -> GenGroup

    ObjPtr -> DataGroup -> NodeGroup -> StreamGroup

    are a couple of example lineages.

    Lynn
     
    Lynn McGuire, Sep 6, 2011
    #5
  6. Lynn McGuire

    Christopher Guest

    On Sep 6, 2:09 pm, Lynn McGuire <> wrote:
    > On 9/6/2011 12:00 PM, Christopher wrote:
    >
    >
    >
    >
    >
    > > On Sep 6, 11:31 am, Lynn McGuire<>  wrote:
    > >> Does virtualizing all methods slow C++ down ?  We
    > >> virtualize all methods as a matter of course since
    > >> we use groups of objects all over the place.  To
    > >> illustrate this I have included the top portion of
    > >> our header files:

    >
    > >> class GenGroup : public DataGroup
    > >> {
    > >> public:
    > >>      int transferingDataNow;
    > >> public:
    > >>         //  constructor
    > >>      GenGroup ();
    > >>      GenGroup (const GenGroup&  rhs);
    > >>      GenGroup&  operator = (const GenGroup&  rhs);
    > >>         //  destructor
    > >>      virtual ~GenGroup ();
    > >>      virtual GenGroup * clone () { return new GenGroup ( * this); }
    > >>      virtual void makeUnitsInput (InputCollection * anInpCol);
    > >>      virtual void reinitStreamBox (FmFile * FMFile);
    > >>      virtual DataDescriptor * descriptor (int aSymbol, int version);
    > >> ...

    >
    > >> Thanks
    > >> Lynn

    >
    > > If you virtualize one, there is no cost for virtualizing the others.
    > > There is a cost associated with virtualizing one method. See the FAQ.

    >
    > > I wouldn't virtualize every single method in every single class
    > > though. That is just not thoughtful design. When I see a virtual
    > > method, I assume the author INTENDED for the class to be derived from.
    > > Surely, you have a concrete class somewhere.

    >
    > We have so many methods in our 600 classes and 700K lines
    > of code that we are not sure what needs to be virtual and
    > does not.  So we do all to keep from missing 1, 2 or 20.
    >
    > Thanks,
    > Lynn- Hide quoted text -
    >
    > - Show quoted text -



    Bad design doesn't make up for a bad process.

    Surely, you have unit tests for these classes? Surely those tests
    would show whether the expected behavior of a method call resolving to
    the derived vs the base is occurring?
     
    Christopher, Sep 6, 2011
    #6
  7. Lynn McGuire

    jacob navia Guest

    Le 06/09/11 21:13, Lynn McGuire a écrit :
    > Thanks, I had not thought about the direct calls just
    > directly binding to the proper method. I'll bet that
    > there are few direct calls in our code though as we
    > have many children classes.
    >
    > ObjPtr -> DataGroup -> GenGroup
    >
    > ObjPtr -> DataGroup -> NodeGroup -> StreamGroup
    >
    > are a couple of example lineages.
    >
    > Lynn


    This is completely ridiculous. An indirect call vs a direct call
    will cost you some pipeline turbulence and the cache may be flushed,
    really not a big deal.

    Have you MEASURED the slowdown? Is it significant in relationship
    to OTHER improvements in your code?

    Have you profiled your code to KNOW where it is spending 90% of the
    time?

    Before you answer THOSE questions, worrying about direc/indirect
    calls is WASTED time and effort.
     
    jacob navia, Sep 6, 2011
    #7
  8. jacob navia <> writes:

    > Le 06/09/11 21:13, Lynn McGuire a écrit :
    >> Thanks, I had not thought about the direct calls just
    >> directly binding to the proper method. I'll bet that
    >> there are few direct calls in our code though as we
    >> have many children classes.
    >>
    >> ObjPtr -> DataGroup -> GenGroup
    >>
    >> ObjPtr -> DataGroup -> NodeGroup -> StreamGroup
    >>
    >> are a couple of example lineages.
    >>
    >> Lynn

    >
    > This is completely ridiculous. An indirect call vs a direct call
    > will cost you some pipeline turbulence and the cache may be flushed,
    > really not a big deal.


    Depends on the code. With lots of small methods (a la Java, with
    getters/setters all over the place), it may be significant, mainly
    because of the impossibility to inline. Take any program, compile it
    with inlining disabled: the difference may be significant.

    > Have you MEASURED the slowdown? Is it significant in relationship
    > to OTHER improvements in your code?


    Hard to measure. You need to change tens/hundreds of classes...

    > Have you profiled your code to KNOW where it is spending 90% of the
    > time?


    In the case I mention above (many small functions), the overhead would
    be spread over a large part of the code. Profiling becomes less useful.

    > Before you answer THOSE questions, worrying about direc/indirect
    > calls is WASTED time and effort.


    I've just done this on a piece code that was evaluating an expression
    tree on each vertex of a 4D grid, with an insane number of
    floating-point ops. Incredible speedup (~120). Granted, the original was
    over-engineered, and this may be a corner case.

    Regarding Lynn's case, I would say you're right: he may be better off
    keeping his current design safe rather than trying to save a few cycles.

    -- Alain.
     
    Alain Ketterlin, Sep 6, 2011
    #8
  9. Lynn McGuire

    Lynn McGuire Guest

    On 9/6/2011 2:57 PM, jacob navia wrote:
    > Le 06/09/11 21:13, Lynn McGuire a écrit :
    >> Thanks, I had not thought about the direct calls just
    >> directly binding to the proper method. I'll bet that
    >> there are few direct calls in our code though as we
    >> have many children classes.
    >>
    >> ObjPtr -> DataGroup -> GenGroup
    >>
    >> ObjPtr -> DataGroup -> NodeGroup -> StreamGroup
    >>
    >> are a couple of example lineages.
    >>
    >> Lynn

    >
    > This is completely ridiculous. An indirect call vs a direct call
    > will cost you some pipeline turbulence and the cache may be flushed, really not a big deal.
    >
    > Have you MEASURED the slowdown? Is it significant in relationship
    > to OTHER improvements in your code?
    >
    > Have you profiled your code to KNOW where it is spending 90% of the
    > time?
    >
    > Before you answer THOSE questions, worrying about direc/indirect
    > calls is WASTED time and effort.


    I am not planning on changing our design, it works very well
    and gets an amazing amount of work done in the amount of
    code written. After reading the "Generally, are the programs
    written by C++ slower than written by C 10% ?" thread, I was
    just wondering about the major difference between C and C++
    in our code. In the long and short runs, I would say that
    the overhead is more than worth the automatic object method
    lookups. More than !

    Thanks,
    Lynn
     
    Lynn McGuire, Sep 6, 2011
    #9
  10. Lynn McGuire

    Lynn McGuire Guest

    On 9/6/2011 3:40 PM, Alain Ketterlin wrote:
    > jacob navia<> writes:
    >
    >> Le 06/09/11 21:13, Lynn McGuire a écrit :
    >>> Thanks, I had not thought about the direct calls just
    >>> directly binding to the proper method. I'll bet that
    >>> there are few direct calls in our code though as we
    >>> have many children classes.
    >>>
    >>> ObjPtr -> DataGroup -> GenGroup
    >>>
    >>> ObjPtr -> DataGroup -> NodeGroup -> StreamGroup
    >>>
    >>> are a couple of example lineages.
    >>>
    >>> Lynn

    >>
    >> This is completely ridiculous. An indirect call vs a direct call
    >> will cost you some pipeline turbulence and the cache may be flushed,
    >> really not a big deal.

    >
    > Depends on the code. With lots of small methods (a la Java, with
    > getters/setters all over the place), it may be significant, mainly
    > because of the impossibility to inline. Take any program, compile it
    > with inlining disabled: the difference may be significant.
    >
    >> Have you MEASURED the slowdown? Is it significant in relationship
    >> to OTHER improvements in your code?

    >
    > Hard to measure. You need to change tens/hundreds of classes...
    >
    >> Have you profiled your code to KNOW where it is spending 90% of the
    >> time?

    >
    > In the case I mention above (many small functions), the overhead would
    > be spread over a large part of the code. Profiling becomes less useful.
    >
    >> Before you answer THOSE questions, worrying about direc/indirect
    >> calls is WASTED time and effort.

    >
    > I've just done this on a piece code that was evaluating an expression
    > tree on each vertex of a 4D grid, with an insane number of
    > floating-point ops. Incredible speedup (~120). Granted, the original was
    > over-engineered, and this may be a corner case.
    >
    > Regarding Lynn's case, I would say you're right: he may be better off
    > keeping his current design safe rather than trying to save a few cycles.
    >
    > -- Alain.


    You are more than correct, I was just wondering. In fact, we
    saved millions of cycles by porting our code from Smalltalk
    to C++. We had to do it anyway, our Smalltalk variant was
    Win16 (don't even go there !) without a Win32 port. I would
    not be surprised if we got a 100X speedup by converting to
    C++ and Win32. Our current design is solid and will not be
    changed - we have many other dragons to slay.

    Thanks,
    Lynn
     
    Lynn McGuire, Sep 6, 2011
    #10
  11. On 09/06/2011 02:35 PM, Christopher wrote:
    > On Sep 6, 2:09 pm, Lynn McGuire<> wrote:
    >> On 9/6/2011 12:00 PM, Christopher wrote:
    >>
    >>
    >>
    >>
    >>
    >>> On Sep 6, 11:31 am, Lynn McGuire<> wrote:
    >>>> Does virtualizing all methods slow C++ down ? We
    >>>> virtualize all methods as a matter of course since
    >>>> we use groups of objects all over the place. To
    >>>> illustrate this I have included the top portion of
    >>>> our header files:

    >>
    >>>> class GenGroup : public DataGroup
    >>>> {
    >>>> public:
    >>>> int transferingDataNow;
    >>>> public:
    >>>> // constructor
    >>>> GenGroup ();
    >>>> GenGroup (const GenGroup& rhs);
    >>>> GenGroup& operator = (const GenGroup& rhs);
    >>>> // destructor
    >>>> virtual ~GenGroup ();
    >>>> virtual GenGroup * clone () { return new GenGroup ( * this); }
    >>>> virtual void makeUnitsInput (InputCollection * anInpCol);
    >>>> virtual void reinitStreamBox (FmFile * FMFile);
    >>>> virtual DataDescriptor * descriptor (int aSymbol, int version);
    >>>> ...

    >>
    >>>> Thanks
    >>>> Lynn

    >>
    >>> If you virtualize one, there is no cost for virtualizing the others.
    >>> There is a cost associated with virtualizing one method. See the FAQ.

    >>
    >>> I wouldn't virtualize every single method in every single class
    >>> though. That is just not thoughtful design. When I see a virtual
    >>> method, I assume the author INTENDED for the class to be derived from.
    >>> Surely, you have a concrete class somewhere.

    >>
    >> We have so many methods in our 600 classes and 700K lines
    >> of code that we are not sure what needs to be virtual and
    >> does not. So we do all to keep from missing 1, 2 or 20.
    >>
    >> Thanks,
    >> Lynn- Hide quoted text -
    >>
    >> - Show quoted text -

    >
    >
    > Bad design doesn't make up for a bad process.
    >
    > Surely, you have unit tests for these classes? Surely those tests
    > would show whether the expected behavior of a method call resolving to
    > the derived vs the base is occurring?


    .... and surely you have a compiler that is able to warn you when a
    derived class overrides a non-virtual member function in one of its base
    classes?
     
    Kevin P. Fleming, Sep 6, 2011
    #11
  12. > On Sep 6, 11:31 am, Lynn McGuire<> wrote:
    >> Does virtualizing all methods slow C++ down ?
    >> [...]


    At 06.09.2011 19:00, Christopher wrote:
    > If you virtualize one, there is no cost for virtualizing the others.
    > There is a cost associated with virtualizing one method. See the FAQ.


    That's not true.

    It is true that the size of an object increases for the first virtual
    function you add, but not for further virtual functions (for usual
    vpointer implementations).
    However, _every_ function might be slower when called virtually, because
    the compiler has less potential for optimization (function inlining etc)
    than for non-virtual functions.

    --
    Thomas
     
    Thomas J. Gritzan, Sep 7, 2011
    #12
  13. Lynn McGuire

    Nobody Guest

    On Tue, 06 Sep 2011 10:00:56 -0700, Christopher wrote:

    > If you virtualize one, there is no cost for virtualizing the others.
    > There is a cost associated with virtualizing one method. See the FAQ.


    That only deals with the memory cost. There is also a potential speed cost
    for every virtual method call.
     
    Nobody, Sep 7, 2011
    #13
  14. Lynn McGuire <> wrote:
    > Does virtualizing all methods slow C++ down ?


    The cost of calling a virtual function (compared to calling a normal
    member function) is often greatly exaggerated. I once tested the speed
    difference myself and the difference was not measurable (in other words,
    both tests returned time values that were so close together that even
    the variance between runs of the same test were larger).

    Of course my sample size is that of 1 (iow. I have tested it only in
    one system with one compiler, using one specific test), so the result is
    far from definitive. However, it does tell me that the difference is
    quite minuscule in practice, and often overwhelmed by everything else
    that happens when calling a function (pushing parameters onto the stack
    and so on, which, depending on the amount and type of parameters, will
    probably take a lot more clock cycles than the indirect virtual function
    call.)

    However, this is comparing virtual functions with regular *non-inlined*
    functions. Function inlining can have a much greater difference, and if
    you virtualize an inline function, it might slow it down significantly
    in comparison. (Either the compiler will not inline it at all, or else
    it will have to add some kind of runtime conditional in the compiled code
    to check if the function has been overridden.) Thus if you have for example
    some really small getter/setter functions that are better inline and really
    don't need to be virtual, don't make them.
     
    Juha Nieminen, Sep 7, 2011
    #14
  15. Lynn McGuire

    Goran Guest

    On Sep 6, 6:31 pm, Lynn McGuire <> wrote:
    > Does virtualizing all methods slow C++ down ?  We
    > virtualize all methods as a matter of course since
    > we use groups of objects all over the place.


    Yes, it's obviously slower to go through a virtual method than not.
    Compiler might turn some of those into non-virtual and even inline it.
    As others have said, you probably can't notice the slowdown. Not
    unless something really degenerate is happening in the code (like,
    you're spending most of the complete program time virtually calling
    functions that are very shot and aren't virtual "in practice").

    However, going "all virtual" is IMO pretty dumb. While it might look
    as "designing for the future", in practice, it's nothing of the sort.
    I think that you should try looking at your virtual functions and
    check how many of them are actually overridden in derived classes, and
    how. I bet you that overridden version can either be trivially
    refactored back into a base class, or it requires so much knowledge of
    the base's internals that it's going impossible to change the base
    without affecting all derived classes. That "coupling through
    inheritance" is poor design.

    Goran
     
    Goran, Sep 7, 2011
    #15
  16. Goran <> wrote:
    > Yes, it's obviously slower to go through a virtual method than not.


    How "obviously"? Do you have any actual measurement results?
     
    Juha Nieminen, Sep 7, 2011
    #16
  17. Lynn McGuire

    Jorgen Grahn Guest

    On Tue, 2011-09-06, Lynn McGuire wrote:
    ....
    > You are more than correct, I was just wondering. In fact, we
    > saved millions of cycles by porting our code from Smalltalk
    > to C++.


    That explains a lot about that "virtual everywhere" design. Go look in
    "The C++ Programming Language"; Stroustrup has a few things to say
    about using C++ as if it was Smalltalk.

    > Our current design is solid and will not be
    > changed - we have many other dragons to slay.


    It may not be highest priority, but I still think there's something
    seriously wrong with having the code this way -- especially if you
    have design rules to forbid normal C++ in new or rewritten code.

    My guess (which is only a guess) is that the worst penalty is that it
    discourages the use of small classes, wrappers and helper functions
    which would make the code more readable. Inlining makes such things
    cost-free, but if you don't have inlining you probably think twice
    before doing such simplifications (at least I do).

    /Jorgen

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .
     
    Jorgen Grahn, Sep 7, 2011
    #17
  18. Lynn McGuire

    Jorgen Grahn Guest

    On Wed, 2011-09-07, Juha Nieminen wrote:
    > Goran <> wrote:
    >> Yes, it's obviously slower to go through a virtual method than not.

    >
    > How "obviously"? Do you have any actual measurement results?


    Surely it's obvious? Even TC++PL says so in various places.

    If it's /measurably slower/ in a real situation is another thing. I
    guess it very rarely is -- except for the missed opportunities for
    inlining which many have mentioned.

    /Jorgen

    --
    // Jorgen Grahn <grahn@ Oo o. . .
    \X/ snipabacken.se> O o .
     
    Jorgen Grahn, Sep 7, 2011
    #18
  19. Lynn McGuire

    Goran Guest

    On Sep 7, 9:57 am, Juha Nieminen <> wrote:
    > Goran <> wrote:
    > > Yes, it's obviously slower to go through a virtual method than not.

    >
    >   How "obviously"? Do you have any actual measurement results?


    Obviously, no ;-). Do you really think it's the same/faster?

    I was merely alluding to the usual implementation with call
    instruction being read off the virtual method table. Hopping twice
    (more, really) can't be faster than once :).

    Goran.
     
    Goran, Sep 7, 2011
    #19
  20. Goran <> wrote:
    > On Sep 7, 9:57 am, Juha Nieminen <> wrote:
    >> Goran <> wrote:
    >> > Yes, it's obviously slower to go through a virtual method than not.

    >>
    >>   How "obviously"? Do you have any actual measurement results?

    >
    > Obviously, no ;-). Do you really think it's the same/faster?


    "Obviously" just could as well mean "significantly" (iow. the slowdown
    is "obvious", ie. very noticeable).

    From my own tests there's no practical difference between a virtual
    function call and a regular function call. Even if there's a clock cycle
    or two of difference, it's so small that this difference gets completely
    buried under everything else that is happening.
     
    Juha Nieminen, Sep 7, 2011
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. PeterOut

    Does Casting Slow a Program Down?

    PeterOut, Jan 31, 2007, in forum: C Programming
    Replies:
    23
    Views:
    698
    Barry Schwarz
    Feb 4, 2007
  2. Kenneth McDonald
    Replies:
    5
    Views:
    325
    Kenneth McDonald
    Sep 26, 2008
  3. Saqib Ali
    Replies:
    6
    Views:
    148
    Erwin Moller
    Feb 20, 2004
  4. PerlFAQ Server
    Replies:
    0
    Views:
    97
    PerlFAQ Server
    Jan 17, 2011
  5. PerlFAQ Server
    Replies:
    0
    Views:
    105
    PerlFAQ Server
    Mar 13, 2011
Loading...

Share This Page