6 ms·
Reading the title I'm worried, should I keep using reflection instead?
by kubb 5y ago
Reading the title I'm worried, should I keep using reflection instead?
- AYBABTME 5y agoUse generics if it makes your dev experience better. Profile if it's slow. Optimize the slow bits.
- LanceH 5y agoIf you're worried you should benchmark the differences on your requirements.
- morelisp 5y agoIf you're using reflection or storing a bare interface{}, you should probably instead try using generics. If you're using real interfaces, you should keep using interfaces. If you care about performance, you should not try to write Java-Streams / FP-like code in a language with no JIT and a non-generational non-compacting GC.
- linkdd 5y agoPremature optimization is a bad thing. Just implement naively, then if you have performance issues identify the bottleneck.
- morelisp 5y agoIgnorance of how your language works is a bad thing. Knowing where performance issues with certain techniques might arise is not premature optimization. Implement with an appropriate level of care, including performance concerns. Not every kind of poor performance appears as a clear spike in a call graph, and even fewer can be fixed without changing any external API.
- 8note 5y agoThe language is a tool for a job. If I'm using low torque, I don't need to know the yield strength of my wrench
- linkdd 5y ago> Ignorance of how your language works is a bad thing. And I never said anything remotely close to contradict this statement. > Knowing where performance issues with certain techniques might arise is not premature optimization. It is: - Python: should I use a for loop, a list comprehension or the map function? - C++: should I use a std::list, std::vector, ...? - Go: should I use interface{} or generics? The difference between those options is subtle and completely unrelated to the problem you want to solve. > Implement with an appropriate level of care, including performance concerns. Step 1: solve your problem naively, aka: make it work Step 2: add tests, separate business logic from implementation details, aka: make it right Step 3: profile / benchmark to see where the chokepoints are and optimize them, aka: make it fast Chances are that if you have deeply nested loops, generics vs interface{} will be the last of your problems. To take the C++ example again, until you have implemented your algorithm, you don't know what kind of operations (and how often) you will do with your container. So you can't know whether std::list or std::vector fits best. In Go, until you have implemented your algorithm, you don't know how often you will have to use generics / reflection, so you can't know what will be the true impact on your code. The "I know X is almost always faster so i'll use it instead of Y" will bite you more often than you can count. > Not every kind of poor performance appears as a clear spike in a call graph CPU usage, memory consumption, idling/waiting times, etc... Those are the kind of metrics you care about when benchmarking your code. No one said you only look at spike in a call graph. But still, to look for such information, you need to have at least a first implementation of your problem's solution. Doing this before is a waste of time and energy because 80% of the time, your assumptions are wrong. > and even fewer can be fixed without changing any external API. This is why you "make it work" and "make it right" before you "make it fast". This way you have a clear separation between your API and your implementation details.
- morelisp 5y agoYou're giving fine advice for well-scoped tasks with minimal design space (well, sort of - using std::list ever is laughable - but if you had said unordered_map vs. map, sure, so I take the broad point). But, some of us have been around the block a few times though, and now need to make sure those spaces are delineated for others in a way that won't force them into a performance corner. > until you have implemented your algorithm, you don't know what kind of operations (and how often) you will do with your container.. until you have implemented your algorithm, you don't know how often you will have to use generics / reflection, so you can't know what will be the true impact on your code. I don't mean to brag, but I guess I'm a lot better at planning ahead than you. I don't usually have the whole program written in my head before I start, but I also can't remember any time I had to reach for a hammer as big as reflect and didn't expect to very early on, and most of the time I know what I intend to do to my data! > This is why you "make it work" and "make it right" before you "make it fast"... This way you have a clear separation between your API and your implementation details. This is not possible. APIs force performance constraints. Maybe wait until your API works before micro-optimizing it, but also maybe think about how many pointers you're going to have to chase and methods your users will need to implement in the first place because you probably don't get to "optimize" those later without breaking the API. You write about "the bottleneck", but there's not always a single bottleneck distinct from "the API". Sometimes there's a program that's slow because there's a singular part that takes 10 seconds and could take 1 second. But sometimes it's slow because every different bit of it is taking 2ns where it could take 1ns. Consider the basic read-some-bytes API in Go vs. Python (translated into Go, so the difference is obvious): type GoReader interface { Read([]byte) (int, error) } type PyReader interface { Read(int) ([]byte, error) } You're never going to make an API like PyReader anywhere near as fast as GoReader, no matter how much optimization you do!
- jerf 5y agoIf the information in this article is make-or-break for your program, you probably shouldn't have chosen Go. In the grand space of all programming languages, Go is fast. In the space of compiled programming languages, it's on the slower end. If you're in a "counting CPU ops" situation it's not a good choice. There is an intermediate space in which one is optimizing a particular tight loop, certainly, I've been there, and this can be nice to know. But if it's beyond "nice to know", you have a problem. I don't know what you're doing with reflection but the odds are that it's wildly slower than anything in that article though, because of how it works. Reflection is basically like a dynamically-typed programming language runtime you can use as a library in Go, and does the same thing dynamically-typed languages (modulo JIT) do on their insides, which is essentially deal with everything through an extra layer of indirection. Not just a function call here or there... everything. Reading a field. Writing a field. Calling a function, etc. Everywhere you have runtime dynamic behavior, the need to check for a lot of things to be true, and everything operating through extra layers of pointers and table structs. Where the article is complaining about an extra CPU instruction here and an extra pointer indirection there, you've signed up for extra function calls and pointer indirections by the dozens. If you can convert reflection to generics it will almost certainly be a big win. (But if you cared about performance you were probably also better off with an interface that didn't fully express what you meant and some extra type switches.)
- shadowgovt 5y agoThis is good high-level advice as well as low-level advice. Go is positioned to be most useful as an alternative to Java, and to C++ where performance isn't the key factor (i.e. projects where C++ would be chosen because "Enh, it's a big desktop application and C++ is familiar to a lot of developers," not because the project actually calls for being able to break out into assembly language easily or where fine-tuning performance is more important than tool-provided platform portability).
- azth 5y agoIn practice, it's used as an alternative to python and ruby and nodejs. It can't fully do what Java or C# do.
- lalaithion 5y agoVery few people are actually answering your question, so I'll answer it: Generics are slower than concrete types, and are slower than simple interfaces. However, the article does not bother to compare generics with reflection, and my intuition says that generics will be faster than reflection.
- pdpi 5y agoDefinitely not. In the general case, you will make things simpler and faster by turning reflection-based code into generic code. What this article says is that a function that is generic on an interface introduces a tiny bit of reflection (as little as is necessary to figure out if a type conforms to an interface and get an itab out of it), and that tiny bit of reflection is quite expensive. This means two things. One, if you're not in a position where you're worried about what does or does not get devirtualized and inlined, this isn't a problem for you. If you're using reflection at all, this definitely doesn't apply to you. Two, reflection is crazy expensive, and the whole point of the article is that the introduction of that tiny bit of reflection can make function calls literally twice as slow. If you are in a position where you care about the performance of function calls, you're never really going to improve upon the situation by piling on even more reflection.