Announcement

Collapse
No announcement yet.

Mono 2.11 Release Brings Many Changes

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • #61
    Originally posted by ciplogic
    but also in C#, as the iteration will do every time a virtual call, as collections have to implement IEnumerable<T>
    I'm watching the discussion from the sidelines, but right now I have to point out that this is actually incorrect. Iteration of standard collections does not perform a virtual call, unless you explicitly cast them to IEnumerable<T>. If you check the code for e.g. System.Collection.Generic.List<T> you will find that it offers two implementations of GetEnumerator():
    Code:
    IEnumerator<T> IEnumerable<T>.GetEnumerator();
    public List<T>.Enumerator GetEnumerator();
    The first is an explicit interface implementation, while the second is a regular function. There is a little-known detail in the C# spec which states that when both an interface implementation and a regular function are available, the compiler will prefer to call the regular function - hence bypassing the cost of a virtual function call. (Not only that, but in the case of standard collections, iterating directly over the collection will avoid the boxing the return value).

    This is a very important optimization when writing your own collections or any code sensitive to memory allocations. Don't cast to IEnumerable<T> unless you really must!

    LOL, when it's the only one being misused, yes
    No, really not. There is perfectly valid C++ code that is significantly slower than its C# equivalent, for instance boost::signal and Qt signals. Just check the implementations in boost and Qt: to raise a simple signal you execute pages of code, multiple function calls and a few dictionary lookups. In C#, signals (delegate, aka MulticastDelegate) perform like virtual function calls.

    There's simply no comparison, any signal-heavy code right now performs better in C# than it does in C++.

    Comment


    • #62
      Originally posted by ciplogic View Post
      Code:
          previous_message = 0;
          std::cout << *msg << std::endl;
      This would crash! I'm not saying that in places where you "obviously know" that the shared pointer cannot be null, to not use the const& construct, but you are basically on your own when you work with const references! So be aware, and use the principle: "the root of all evil is premature optimization".
      No matter how "idiot proof" you make your code, you will fail badly in the end as there is no limit to human stupidity. For example you could do previous_message->clear() instead of previous_message=0 and mess things up even for the case of passing as value.

      That said, when I write code I don't try make it easy for others that might modify it to do stupid things. That's because the only place were the original pointer can be modified is right in the function that received the const reference. If a function deeper in the stacktrace messed up the pointer it will pass it messed up further no matter if you pass by value or reference. If you assure that all invokes on other thread are called with copies you also make sure no other thread can mess up the pointer. So that only leaves the current function as the culprit. That's the basic definition of shooting yourself in the foot, and C++ offers plenty of possibilities to do it. But just because you can shoot yourself in the foot doesn't mean you actually have to do it, right?

      Originally posted by ciplogic View Post
      You misunderstood the problem,
      The problem is when you have side effects, and try this:
      I was already writing the reply when you posted the other example.

      Originally posted by ciplogic View Post
      Code:
      void resetX() {
        x = std::shared_ptr<int> (0);
      }
      But what if resetX would instead do *x = 0? You just can't be completely safe, and feeling safe only makes the problem worse.
      Last edited by Ansla; 29 March 2012, 11:48 AM.

      Comment


      • #63
        Originally posted by BlackStar View Post
        No, really not. There is perfectly valid C++ code that is significantly slower than its C# equivalent, for instance boost::signal and Qt signals. Just check the implementations in boost and Qt: to raise a simple signal you execute pages of code, multiple function calls and a few dictionary lookups. In C#, signals (delegate, aka MulticastDelegate) perform like virtual function calls.

        There's simply no comparison, any signal-heavy code right now performs better in C# than it does in C++.
        I was strictly combating his argument as a conclusion to our little experiment, I did admit several pages ago, I think in my first message in this thread, that VM languages have an advantage when it comes to reflection, and signals are just a form of reflection. As long as C++ doesn't support reflection at the language level all library only solutions will be slow. But C++ compilers could help with this and hopefully will do that in the future, hopefully using compiler specific extensions first, because I doubt a new standard for C++ will arrive before 2020.

        Comment


        • #64
          Originally posted by BlackStar View Post
          I'm watching the discussion from the sidelines, but right now I have to point out that this is actually incorrect. Iteration of standard collections does not perform a virtual call, unless you explicitly cast them to IEnumerable<T>. If you check the code for e.g. System.Collection.Generic.List<T> you will find that it offers two implementations of GetEnumerator():
          Code:
          IEnumerator<T> IEnumerable<T>.GetEnumerator();
          public List<T>.Enumerator GetEnumerator();
          The first is an explicit interface implementation, while the second is a regular function. There is a little-known detail in the C# spec which states that when both an interface implementation and a regular function are available, the compiler will prefer to call the regular function - hence bypassing the cost of a virtual function call. (Not only that, but in the case of standard collections, iterating directly over the collection will avoid the boxing the return value).

          This is a very important optimization when writing your own collections or any code sensitive to memory allocations. Don't cast to IEnumerable<T> unless you really must!
          Thank you, really informative and nice to know! Mea culpa! (I really did not know that!)
          The boxing problem was in Java only (as ArrayList<Integer> is in fact ArrayList<Object>), which hopefully I did not said it was true for .Net/Mono world.

          Comment


          • #65
            Originally posted by Ansla View Post
            But what if resetX would instead do *x = 0? You just can't be completely safe, and feeling safe only makes the problem worse.
            The C++ folks say: C++ is fast but can be made to be safe. And let the user to make the balance. And later, they get into profiling, and they notice that putting const& it will make the life better, but later they can get wrong pointers.
            In fact you can be "completely safe", in the idea that you don't get to use a deleted pointer in a VM (excluding you call C++ or "unsafe" C# code) and when you get exceptions by default they are more natural to handle: NullPointerException sounds as a sane error (not best to get).
            The "feeling safe" is using smart-pointers originally, and later using const& to "feel fast" and much later having a crash, and because you don't know which pointer did what, you will say: was better without smart pointers all-together.
            Functional languages (let's say Select-SQL) can be safe: they don't have side-effects (do they!?, maybe just making a table view that is a result of a select).
            C# is not fully safe, is a huge step in the right direction (compared with C pointer's arithmetic or C++ techniques to manage memory fast and efficient). Generics are not hard to be used (to enforce type safety), GC is safer (and faster, as we just tested out), you can use functional constructs (Linq), so you can know that you don't have side effects (at least where you use Linq or foreach). The switch statement is striter (you have to put break on every branch), and works with other types (for example with strings). C++11 improved on its own, and I'm glad for this, but still it is an unsafe language if you don't work with templates, and sometimes if you don't look for your back, you will do either too many copies (of vectors, or updating reference counts) or you will share the raw pointer to your vector and you don't know who changed it.

            Comment


            • #66
              Pointers are only a means to an end, VM languages keep your pointers safe from yourself, but are pointers what really matter or the data pointed by them? How does the safety of the pointers help you in this case:

              Code:
              class Dummy {
                      static class Message {
                              String text;
              
                              Message() {
                              }
              
                              Message(String text) {
                                      this.text = text;
                              }
                      }
              
                      static Message previousMessage = new Message();
              
                      static void Send(Message message) {
                              previousMessage.text = "Bye";
                              System.out.println(message.text);
                              previousMessage = message;
                      }
              
                      public static void main(String[] args) {
                              Send(new Message("Hi"));
                              Send(previousMessage);
                      }
              }
              Last edited by Ansla; 29 March 2012, 01:25 PM. Reason: Fix typo

              Comment


              • #67
                Originally posted by Ansla View Post
                Pointers are only a means to an end, VM languages keep your pointers safe from yourself, but are pointers what really matter or the data pointed by them? How does the safety of the pointers help you in this case:
                By refusing to run this code. Java might be so broken that it would run this, but C# would outright refuse: it won't allow you to create instances of static classes or add instance members to a static class (because such code is _always_ wrong).

                Comment


                • #68
                  Originally posted by ciplogic View Post
                  Thank you, really informative and nice to know! Mea culpa! (I really did not know that!)
                  The boxing problem was in Java only (as ArrayList<Integer> is in fact ArrayList<Object>), which hopefully I did not said it was true for .Net/Mono world.
                  You're welcome. The boxing problem in .Net is slightly different: it has to do with boxing the List<T>.Enumerator to a IEnumerator<T>, when iterating over IEnumerator<T> (instead of the concrete List<T>). The .Net people saw that foreach would be inefficient if it had to allocate a heap object everytime it was called, so they made List<T>.GetEnumerator() return a stack-allocated struct (details like this are why C# rocks for performance-sensitive code!) Now, if you use foreach on a List<T> directly you'll be fine. However, if you cast to IEnumerable<T>, then GetEnumerator() returns a generic IEnumerator<T>, so the List<T>.Enumerator struct has to be boxed into the IEnumerator<T> interface.

                  I hope this makes sense.

                  Java (the language) is much more primitive in this regard, but has a more advanced runtime that can make up for some of the overhead. Obviously, neither C# nor Java can reach C++ in such micro-optimizations, but the tables turn when you look at the big picture, at least in my experience.

                  Even though I have roughly double the experience of C++ than C#, I find I can outperform C++ code with C# roughly 80% of the time, given a similar amount of effort. How can this be? The reason is that C# is a much easier language to develop with, so I can finish the implementation faster and dedicate time to optimization. With C++, just implementing the required features can be challenging enough, that optimization does not even enter the picture.

                  Consider how easy asynchronous programming or task-based parallelism is in C#. C++ is a with the gargantuan pain in the ass in comparison - so much, that your average Linux programmer still writes serial, synchronous code in 2012!

                  Of course, this means that I can consistently outperform C# with C++ another 20% of the time. For instance, anything that has to do with math (vectors, matrices, etc) and bit fiddling (image processing, compression) is better suited to C++.

                  (Which is why I would kill for features such as constant references, static generic constrains, forced inlining and compiler intrinsics in C# - one can only dream!)

                  Comment


                  • #69
                    Originally posted by BlackStar View Post
                    (Which is why I would kill for features such as constant references, static generic constrains, forced inlining and compiler intrinsics in C# - one can only dream!)
                    Don't kill anyone, you can give (in .Net 4.5, I'm not sure if Mono takes them in account these hints:
                    Originally posted by ciplogic View Post
                    Yes, in .Net 4.5/latest Mono, but are a fairly new development.
                    Also you may want to try this - if you want to optimize .Net 4.5 launch performance (and to get a good performance overall) another .Net 4.5 feature.

                    Comment


                    • #70
                      Originally posted by BlackStar View Post
                      By refusing to run this code. Java might be so broken that it would run this, but C# would outright refuse: it won't allow you to create instances of static classes or add instance members to a static class (because such code is _always_ wrong).
                      So change it to use a non-static code if C# doesn't like it, the problem will still be there. Java passes parameters "as value", but what it actually passes as value is a reference, so you can make changes to the object using either of the references. As far as I know C# does the same thing, and I'm not aware of any way to pass the actual data as value in either of the languages, though my knowledge of VM based languages is limited so please correct if I'm wrong and you can replicate what the following C++ function does:

                      Code:
                      struct Message {
                          std::string text;
                      };
                      
                      void PrintMessage(Message msg) {
                          std::cout << msg.text << std::endl;
                      }
                      Whatever the caller does, if msg.text contains "Hi" when the function is called that's what will appear on the screen. Unless the function itself changes msg.text, but that's no side effect, if a programmer did that and didn't expect the result it's time to look for another profession, no language can be that idiot proof.
                      Last edited by Ansla; 29 March 2012, 02:57 PM. Reason: add quote

                      Comment

                      Working...
                      X