4 ms·
If you are using Julia, then I will presume you are primarily working in some form of numerical computing. It is a very wide field, so it is difficult to say i
by cjhanks 10y ago
If you are using Julia, then I will presume you are primarily working in some form of numerical computing. It is a very wide field, so it is difficult to say if object ownership will be tricky or not.
If you are working with (b|m)illions of particles or entities which are capable of changing ownership, you will have a hard time in any language. Whether that's the C++ ownership being unclear or the garbage collector defragmenting your memory (just because you don't see the issue doesn't means it's gone in GC languages).
That said, typically I find most algorithms have a computable upper-bound memory constraint and so you just pre-allocate a slab and then objects reference it. You will usually get better cache coherency and less wasted operations/sec. You're left with only a couple pointers you need to delete when you're done with the structure.
As for `functional programming`, it depends on what you're looking for. C++ has two killer features for this, in my opinion; expression templates can seriously improve performance of functional composition (see Eigen3), and compile-time elision of type-dependent code branches (like a static if).
Since I have learned to use `unique_ptr`, `shared_ptr`, `weak_ptr`, (`deferred_ptr`), and to prefer idiomatic accessors like `.at`, `.find`, `.insert` I cannot recall the last time Valgrind was angry at me. I think I end up writing `new` and `delete` maybe once a month(?).
If I was designing widgets or applications with hard to define user interaction, I imagine I would have more complaints to share.
- ced 10y agoInteresting, thank you for the detailed answer. Expression templates do look very intriguing and powerful, I wonder what kind of application it enable. Julia has several cool performance-oriented constructs too: generated functions, loop fusion, JIT compilation (like your static if), and immutables (http://julialang.org/blog/2013/03/efficient-aggregates http://julialang.org/blog/2013/03/efficient-aggregates). It has a lot of potential, but then, so do other performance-oriented languages. It'll be interesting to do comparisons in a few years when the dust has settled a bit.
- srean 10y ago> Expression templates do look very intriguing and powerful, I wonder what kind of application it enable. Fast chain of operations on an array was one of the primary motivations. You can take a look at Blitz++, I believe it was the first library to push that idea. But ET is not limited to numerical operations on an array, you could do query optimization, optimization of string concatenation, essentially any application where deforestation of parse trees have the potential to make things faster.
- KKKKkkkk1 10y agoCuriously enough, linear algebra is the canonical application for expression templates, and yet if you ask a linear algebra expert, they will tell you that expression templates bring no benefits to 99% of linear algebra computations and make your code complex and bug prone. For examples of how expression templates can screw you up in unexpected ways, see: https://eigen.tuxfamily.org/dox/TopicLazyEvaluation.html https://eigen.tuxfamily.org/dox/TopicLazyEvaluation.html https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenType... https://eigen.tuxfamily.org/dox/TopicPitfalls.html https://eigen.tuxfamily.org/dox/TopicPitfalls.html
- cjhanks 10y agoIt depends on the codebase you are working with. Prior to 'auto' it was difficult to impossible to annotate expressions (thus forcing evaluation on assignment). Many people still believe "more lines of code = more slow", they will have problems. Functional non-template boundaries are also slow... any time you force evaluation you will see slow down. My experience... sometimes I have to pass through multiple reference frames to get to the correct affine transformation. I have three options: manually derive the transform (only works for a finite number of well understood transformations), waste BLAS ops on transform concatenation, or let expression templates fold numerical operations to the solution (often with numerical precision benefits btw).
- KKKKkkkk1 10y agoCan you explain what you're getting at? I've spoken to people who authored expression template libraries and they say that mixing auto with expressions is a worst practice. You're setting yourself up for bugs. See the third link above. As to chaining affine transformations, how is letting the expression-template library evaluate a chain of transformations different from doing so explicitly? Would you agree that the computation boils down to the same flops either way?
- cjhanks 10y ago
- int_19h 10y agoOne nifty trick to avoid writing new or delete even when you need manual heap allocation is to always use std::make_unique (or std::make_shared, as needed) instead. That way you're forced to spell out any transfer of ownership as it is happening, and so someone's always be sure to delete the object. You also dodge the bullet with indeterminate ordering of new's and constructors, that might result in memory leaks if one of the constructors throws. An important extension to this trick is that unique_ptr and shared_ptr are specialized for arrays. So e.g. to allocate a dynamic array of size n, you can do: auto a = std::make_unique<char[]>(100); or auto a = std::make_unique<int[3]>(1, 2, 3); and then use it mostly like an array - a[i] is overloaded and work as expected, for example. The reason why you'd want to do this rather than using std::vector is because you don't get the overhead of overallocation (since vectors distinct capacity and size) and default-initialization of the elements. With these, it's quite possible to write >100 kLoC of C++ without a single new or delete in sight.
- shermanyo 10y agoGreat tip, thanks for the clear example :)