Announcement

Collapse
No announcement yet.

Rust For Linux Kernel v9 Patches Trim Things Down Greatly For Easier Upstreaming

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

  • mdedetrich
    replied
    Originally posted by kpedersen View Post

    Indeed. Rust would have failed very hard back then. But now Linus has commercial companies to keep happy.
    Thats completely not the reason why C++ didn't accepted by Linus and Rust did. If you read through the mailing list, you will see that Linus's primary reason for not using C++ was due to language design (i.e. exceptions, OO, overly complex) and bad implementations by compilers. He also didn't like the default attitude of C++ programmers.

    He accepted Rust primarily because he thinks its a better designed language compared to C++ and it solves some real problems that C/C++ doesn't (i.e. proper memory safety).
    Last edited by mdedetrich; 08 August 2022, 08:23 AM.

    Leave a comment:


  • NobodyXu
    replied
    Originally posted by ultimA View Post

    Yes, in C++ you can init a vector not just with the number of elements, but also with specific values for each element. And you are misusing this feature as a bug to claim it is a design deficiency?

    The coder could have written:

    Code:
    auto v = std::vector<int>(10);
    Or, to explicitly communicate intent and avoid any misunderstandings:

    Code:
    std::vector<int> v;
    v.insert(v.begin(), 10, 0);
    Or, probably what he really wanted:

    Code:
    std::array<int, 10> arr;
    There are parts of the C++ language that are actually weird, hard or confusing (but that doesn't mean they need to be used or cannot be abstracted away). What you show in your example though is not one of those parts, it is just a coding bug.
    It's a bug that demonstrates how confusing C++ is and how poorly it is designed.
    If you have to check all constructors of a class just to create an object, be extra careful with the "{}"/"()" when constructing an object, be extra careful with initializer list, then clearly the language is too complicated.

    Oh and one has to use exception if the constructor can fail, because there's no other way to express that.
    If you don't want exception, then just go back to static method.

    That just feels increditably awkward to me that anybody needs low-latency and high performance uses a strange subset of C++ without exceptions, RTTI and sometimes avoid constructor if the creation of an object can fail and it has become the de-factor standard for them to not use these parts of C++.

    And if you want a vector that can take advantage of realloc/mremap, then you have to use folly's vector which specialise for POD and other registered types so that it is sure it is safe to use realloc/mremap.

    Leave a comment:


  • NobodyXu
    replied
    Originally posted by ultimA View Post
    The case with multiple polymorphic objects of different types in the same container is a use-case that simply needs vtables. Rust would need it too in this case, so this is an invalid critique. Same goes for reference counting, you only need vtables there if you want to provide polymorphic reference counted pointers, and in that case Rust cannot get around that either. In C++ all you'd need is a smart pointer type that is not yet part of the STL, and bam you have refcounting without dynamic dispatch. Both C and Rust have or will have standard utils/library functions to support kernel devs, so a pointer class for C++ to be added like this wouldn't be unprecedented.
    I agree that in these cases, you need vtables none the less.

    Though my point is just that virtual function in C++ is not zero-cost, since the objects have the 8-bytes vtable even if you don't use it.
    It can be optimized out, but that optimization isn't guarantee to happen.

    Consider the following code:

    Code:
    struct C {
        public virtual void f() {
            /* Contains so much code that compiler does not inline this function */
        }
    }
    
    void large_func(C &c) {
        /* Contains so much code that compiler does not inline this function */
    }
    In this case, the compiler would not be able to inline large_func and cannot inline C::f, so the compiler cannot optimize out the vtable pointer here.

    Originally posted by ultimA View Post
    As I said, you can reliably trigger this optimization on any compiler today, be it clang, gcc or msvc (not as if msvc would be relevant for kernel dev). So what's the problem with relying on it? All released code today, including the Linux kernel, already rely heavily on existing compiler optimizations to achieve acceptable performance. Try compiling the kernel with optimizations turned off and you'll be shocked by your system's performance after that. As long as optimizations work more-or-less reliably, there is nothing wrong relying on them, and we already do this in all performance critical applications and libraries (including the kernel). Why make an exception for function devirtualization? Why is it bad to rely on function devirtualization but not bad to rely on (for example) constant propagation, branch elimination or instruction scheduling? Again, you are artificially placing constraints just so you can prove C++ is worse in this specific regard.
    You misunderstand me again.

    I'm not saying that you cannot depend on optimization, I am simply saying that in C++, virtual function is not zero-cost.
    Optimization here does not change the fact that it isn't zero-cost as it cannot eliminate the unused vtable pointer in all scenarios.

    Leave a comment:


  • ultimA
    replied
    Originally posted by NobodyXu View Post

    As I have said in my previous reply, I don't think it will work well because many data structures are reference counted or put into a container in the kernel.
    If you use dynamic dispatching, then yes, Rust of course use a vtable.
    The case with multiple polymorphic objects of different types in the same container is a use-case that simply needs vtables. Rust would need it too in this case, so this is an invalid critique. Same goes for reference counting, you only need vtables there if you want to provide polymorphic reference counted pointers, and in that case Rust cannot get around that either. In C++ all you'd need is a smart pointer type that is not yet part of the STL, and bam you have refcounting without dynamic dispatch. Both C and Rust have or will have standard utils/library functions to support kernel devs, so a pointer class for C++ to be added like this wouldn't be unprecedented.

    Originally posted by NobodyXu View Post
    My point about vtable not being zero-cost is that if you merely declare a method as "virtual" in C++ or inherit from an abstract base class but just use it as a regular object without dymamic dispatching, then the object still needs to be 8-bytes larger.

    While compiler might be able to optimize it out, it does not guarantee that and it might not be able to optimize it if it is reference counted, put into a container, exposed in a public API or etc.

    Hence it is not zero-cost.

    As I said, you can reliably trigger this optimization on any compiler today, be it clang, gcc or msvc (not as if msvc would be relevant for kernel dev). So what's the problem with relying on it? All released code today, including the Linux kernel, already rely heavily on existing compiler optimizations to achieve acceptable performance. Try compiling the kernel with optimizations turned off and you'll be shocked by your system's performance after that. As long as optimizations work more-or-less reliably, there is nothing wrong relying on them, and we already do this in all performance critical applications and libraries (including the kernel). Why make an exception for function devirtualization? Why is it bad to rely on function devirtualization but not bad to rely on (for example) constant propagation, branch elimination or instruction scheduling? Again, you are artificially placing constraints just so you can prove C++ is worse in this specific regard.

    Leave a comment:


  • ultimA
    replied
    Originally posted by NobodyXu View Post
    Code:
    std::vector<int> v = std::vector{10};
    Yes, in C++ you can init a vector not just with the number of elements, but also with specific values for each element. And you are misusing this feature as a bug to claim it is a design deficiency?

    The coder could have written:

    Code:
    auto v = std::vector<int>(10);
    Or, to explicitly communicate intent and avoid any misunderstandings:

    Code:
    std::vector<int> v;
    v.insert(v.begin(), 10, 0);
    Or, probably what he really wanted:

    Code:
    std::array<int, 10> arr;
    There are parts of the C++ language that are actually weird, hard or confusing (but that doesn't mean they need to be used or cannot be abstracted away). What you show in your example though is not one of those parts, it is just a coding bug.
    Last edited by ultimA; 08 August 2022, 06:16 AM.

    Leave a comment:


  • NobodyXu
    replied
    Originally posted by Sergey Podobry View Post
    That's definitely not for initializing 10 elements with 0 because it has multiple arguments. Of course some language constructions may be misused due to lack of knowledge and experience. It's a valid point for any programming language (including Rust).
    Yes, every language can be misused, but C++ is one that are particular easy to misuse and shoot yourself.
    And its language design is inconsistent and lack of consideration.

    Leave a comment:


  • NobodyXu
    replied
    Originally posted by ultimA View Post
    The way I have described is easy in code and works reliably with current language and compiler tools. Hard for the compiler or not doesn't change the fact that it is already implemented. The only reason this doesn't work in the kernel is because there is no support for compiling C++ code in the kernel today at all.
    As I have said in my previous reply, I don't think it will work well because many data structures are reference counted or put into a container in the kernel.

    Originally posted by ultimA View Post
    Of course if a vtable must be used in C++ due to actually needing dynamic dispatch then there will be an overhead, but Rust cannot get around that either. Even in Rust you either have a vtable in some way (manual or automatic) or you must refrain from using dynamic dispatch. There's no magic for that.
    If you use dynamic dispatching, then yes, Rust of course use a vtable.

    My point about vtable not being zero-cost is that if you merely declare a method as "virtual" in C++ or inherit from an abstract base class but just use it as a regular object without dymamic dispatching, then the object still needs to be 8-bytes larger.

    While compiler might be able to optimize it out, it does not guarantee that and it might not be able to optimize it if it is reference counted, put into a container, exposed in a public API or etc.

    Hence it is not zero-cost.

    Leave a comment:


  • Sergey Podobry
    replied
    Originally posted by ultimA View Post
    Of course if a vtable must be used in C++ due to actually needing dynamic dispatch then there will be an overhead, but Rust cannot get around that either.
    Moreover the Linux kernel already has vtables but implemented in C. With the same overhead as in C++. So it's not a big deal at all if used wisely.

    Leave a comment:


  • Sergey Podobry
    replied
    Originally posted by NobodyXu View Post
    Code:
    #include <utility>
    
    template <class T, class ...Args>
    auto initialize(Args &&...args) -> T {
    T{std::forward(args)...}
    }
    That's definitely not for initializing 10 elements with 0 because it has multiple arguments. Of course some language constructions may be misused due to lack of knowledge and experience. It's a valid point for any programming language (including Rust).

    Leave a comment:


  • ultimA
    replied
    Originally posted by NobodyXu View Post
    Not if the object is reference counted or is put into some kinds of container, which is extremely hard for compiler to remove the vtable pointer.
    And the Linux kernel supports kernel module and anything used in the API must be stable and the compiler cannot just remove a field from it.

    Thus, in practice, this devirtualization might not work that well.
    When you opt-in to use abstract class or virtual method in C++, you must consider its cost and cannot expect it to be zero-cost without some analysis on its specific use cases.
    The way I have described is easy in code and works reliably with current language and compiler tools. Hard for the compiler or not doesn't change the fact that it is already implemented. The only reason this doesn't work in the kernel is because there is no support for compiling C++ code in the kernel today at all. Of course if a vtable must be used in C++ due to actually needing dynamic dispatch then there will be an overhead, but Rust cannot get around that either. Even in Rust you either have a vtable in some way (manual or automatic) or you must refrain from using dynamic dispatch. There's no magic for that. But you can have interfaces without dynamic dispatch in C++ too as shown.
    Last edited by ultimA; 08 August 2022, 05:31 AM.

    Leave a comment:

Working...
X