Live data from Hacker News

2 is a code smell

tech.puredanger.com

21–30 of 33 posts

Re: 2 is a code smell

#21
post #15

I try to be very pedantic about having the most terse, yet readable code possible in my application. This probably means that if, for now, I only need to know how to add two vectors, I will only include a function that adds two vectors and no more. I do realize there's hidden structure there waiting to be exposed, but programming is largely about elaborating the useful structures. If the author's examples had been in…

In my code, I limit my hardcoded constants to 0, 1, -1, and '\0'. Any other numbers, I give it a named constant, even if it's just defined in a local function scope.

Re: 2 is a code smell

#23

Since we've already descended into anecdotal territory, lemme drop an anecdote. I had a dot-product function which turned out to be a hotspot in some numerical code. The original function looked something like: def dot(v1, v2): return sum(i * j for i, j in zip(v1, v2)) Very functional, very pretty, very straightforward. But it was slow, both in CPython and PyPy. A bit of bytecode analysis and profiling later, we real…

Any reason you didn't just use NumPy? http://docs.scipy.org/doc/numpy/reference/generated/numpy.do...

We considered it, since Numpy was already a dependency, but it was even slower than the current code.

Re: 2 is a code smell

#24

Since we've already descended into anecdotal territory, lemme drop an anecdote. I had a dot-product function which turned out to be a hotspot in some numerical code. The original function looked something like: def dot(v1, v2): return sum(i * j for i, j in zip(v1, v2)) Very functional, very pretty, very straightforward. But it was slow, both in CPython and PyPy. A bit of bytecode analysis and profiling later, we real…

If you spend any time in the Clojure community, I think you'll come to an appreciation that practicality and performance are key principles of its design. The goal is to obtain both high levels of abstraction and performance.

But performance means different things to different people.

If you're writing code where dot products are not something you do a lot of, then keeping a clean, idiomatic functional implementation is probably fine.

If you're writing code where dot products happen a lot, such as on real-time three-dimensional games (which happens to by my oeuvre), then the performance increase of having specific, unrolled implementations is huge.

Re: 2 is a code smell

#25

Earlier quoted context omitted.

If you spend any time in the Clojure community, I think you'll come to an appreciation that practicality and performance are key principles of its design. The goal is to obtain both high levels of abstraction and performance.

But performance means different things to different people. If you're writing code where dot products are not something you do a lot of, then keeping a clean, idiomatic functional implementation is probably fine. If you're writing code where dot products happen a lot , such as on real-time three-dimensional games (which happens to by my oeuvre), then the performance increase of having specific, unrolled implementatio…

It depends on what the language and compiler do for you, the elegant form may actually be faster than your supposed uglier-but-faster optimization. As always, time-test before and (if you've decided it's necessary) after optimizing. I remember reading a warning somewhere in the Clojure docs about how using (get x idx) is much faster than (nth x idx), I don't remember why though.

Re: 2 is a code smell

#26
post #7

If a co-worker told me that my function was "code smell" because it took 2 arguments, I would tell him that he needs more important things to worry about.

If a coworker told me the same thing, I'd ask, "Why?" If they gave me the explanation given in the article, I'd say, "Thank you, that's a nice little improvement. The code is simpler AND more general." Then, I'd go on working, a little bit better of a programmer for it. If I repeated this ritual a few hundred times, I might actually become a halfway-decent programmer. In this field, details matter.

There's a difference between seeing that something can be generalized, optimized, or improved and spending the time to do it.

Knowing that difference and applying judgement is a major differentiator between inexperienced programmers and experienced ones. That, I think, is what heyrhett was getting at.

Re: 2 is a code smell

#27
post #26

Earlier quoted context omitted.

If a coworker told me the same thing, I'd ask, "Why?" If they gave me the explanation given in the article, I'd say, "Thank you, that's a nice little improvement. The code is simpler AND more general." Then, I'd go on working, a little bit better of a programmer for it. If I repeated this ritual a few hundred times, I might actually become a halfway-decent programmer. In this field, details matter.

There's a difference between seeing that something can be generalized, optimized, or improved and spending the time to do it. Knowing that difference and applying judgement is a major differentiator between inexperienced programmers and experienced ones. That, I think, is what heyrhett was getting at.

I don't get it. In the case of the vector-add function, once you've happened to notice the more general solution, implementing it will take a couple of minutes. It's not comparable to an optimization, which would normally increase the risk of code errors -- it is a simplification, which would decrease the risk of code errors.

Two minutes to make a minor simplification and learn a generally good way of approaching things with the tool at hand (clojure)? Count me in.

Re: 2 is a code smell

#28
post #25

Earlier quoted context omitted.

But performance means different things to different people. If you're writing code where dot products are not something you do a lot of, then keeping a clean, idiomatic functional implementation is probably fine. If you're writing code where dot products happen a lot , such as on real-time three-dimensional games (which happens to by my oeuvre), then the performance increase of having specific, unrolled implementatio…

It depends on what the language and compiler do for you, the elegant form may actually be faster than your supposed uglier-but-faster optimization. As always, time-test before and (if you've decided it's necessary) after optimizing. I remember reading a warning somewhere in the Clojure docs about how using (get x idx) is much faster than (nth x idx), I don't remember why though.

This is of course true; ALWAYS profile the code before and after any optimizations, it is quite common for things not to act the way you'd expect (either because of the hardware, the compiler, or even other bits of code you're not aware of, which perhaps relies on certain assumptions).

Re: 2 is a code smell

#29

K handles the general case: 1 1 1 + 2 2 2 3 3 3 (1 1 1; 2 2 2; 3 3 3) + (4 4 4; 5 5 5; 6 6 6) (5 5 5 7 7 7 9 9 9)

As does R :)

    > c(1, 1, 1) + c(2, 2, 2)
    [1] 3 3 3
    > matrix(c(1, 1, 1, 2, 2, 2, 3, 3, 3),
    +        ncol = 3, byrow = TRUE) +
    + matrix(c(4, 4, 4, 5, 5, 5, 6, 6, 6),
    +        ncol = 3, byrow = TRUE)
         [,1] [,2] [,3]
    [1,]    5    5    5
    [2,]    7    7    7
    [3,]    9    9    9
Not quite so concise and elegant but it's pretty straightforward stuff for most R users.

Re: 2 is a code smell

#30

Earlier quoted context omitted.

If you spend any time in the Clojure community, I think you'll come to an appreciation that practicality and performance are key principles of its design. The goal is to obtain both high levels of abstraction and performance.

But performance means different things to different people. If you're writing code where dot products are not something you do a lot of, then keeping a clean, idiomatic functional implementation is probably fine. If you're writing code where dot products happen a lot , such as on real-time three-dimensional games (which happens to by my oeuvre), then the performance increase of having specific, unrolled implementatio…

In clojure, you can solve this by using a variadic macro (instead of a function) to generate the fast, unrolled version. I think pg had a similar example in On Lisp for generating fast code to find points on a bezier curve.
Post reply on HN