Live data from Hacker News

Pycraft: Minecraft engine in Python

github.com

21–30 of 49 posts

Re: Pycraft: Minecraft engine in Python

#21
post #18
post #15

Earlier quoted context omitted.

This is in Python 3. xrange is now range, so only a generator is constructed. Agreed that 1 <= len(vals) < 5 would be more Pythonic.

To be pedantic (which I think is warranted here), range does not return a generator, it returns a sequence called a range object. This object can be indexed, sliced, and (relevant to this discussion) supports the 'in' operator. "x in range(10)" will operate in constant time and memory in Python 3. Whether it is actually more efficient than "0 $ python3 -m timeit -s 'x = 8' 'x in range(10)' 1000000 loops, best of 3: 0…

I'm certain that the call to range is significant there, but if you think of how this actually plays out, it may be doing a naive list compare. if a compare operation is your limiting operation, then the former has to do nine operations (Is x == 0? Is x == 1? ... Is x == 8?) vs precisely two in the latter (is x >= 0? Is x From a formal CS perspective, that's why this is wrong; It's because 'in' is an o(n) operation, while the two comparisons are constant time. It may be abstracted away by the range object overloading, but that's a fairly narrow optimization it's able to pull off.

On modern CPUS, branching operations like compare usually are your problem; An unconditional function call is easy to optimize, but a branch in a looping construct is guaranteed to lead to one or more mispredictions. Trying to minimize the surface area of comparisons is an important part of performant code.

Re: Pycraft: Minecraft engine in Python

#22
post #2

I've always wanted to have an MC clone in Python with GUI event hooks to run scripts. Like pull a lever and an email is sent sort of thing. Will this fork allow for something like that? I'm not against adding it myself if allowed, btw.

You could always give me a hand to get https://github.com/voltagex/minecraft-rebridge up and running again, and then use HTTP from Python to talk to Minecraft-proper ;)

Re: Pycraft: Minecraft engine in Python

#23
post #14

I cringe when I see code like this: if len(vals) in range(1, 5): It seems like a harmless enough thing to do but that code is effectively creating a new array with values [1, 2, 3, 4, 5] and testing if the result of len() is in that array by iterating over it. This check is happening multiple times for each draw call every frame. Sadly I see this kind of stuff in Python all the time and it just adds weight to the arg…

Why do you suspect 1 You can actually get a very good idea for what Python is doing under the hood using the dis module: dis.dis(lambda: 1 > 27 ROT_TWO 28 POP_TOP 29 RETURN_VALUE Compare to: dis.dis(lambda: len(vals) in range(1, 5)) 1 0 LOAD_GLOBAL 0 (len) 3 LOAD_GLOBAL 1 (vals) 6 CALL_FUNCTION 1 9 LOAD_GLOBAL 2 (range) 12 LOAD_CONST 1 (1) 15 LOAD_CONST 2 (5) 18 CALL_FUNCTION 2 21 COMPARE_OP 6 (in) 24 RETURN_VALUE

The bytecode count is, while significant in slower CPython interpreters, not directly an indicator of performance. `LOAD_GLOBAL` and `CALL_FUNCTION` are comparatively expensive opcodes to run.

Re: Pycraft: Minecraft engine in Python

#24

I cringe when I see code like this: if len(vals) in range(1, 5): It seems like a harmless enough thing to do but that code is effectively creating a new array with values [1, 2, 3, 4, 5] and testing if the result of len() is in that array by iterating over it. This check is happening multiple times for each draw call every frame. Sadly I see this kind of stuff in Python all the time and it just adds weight to the arg…

The cost of abstraction.

If it doesn't affect performance then who's thinking is "wrong" here?

Re: Pycraft: Minecraft engine in Python

#25
post #2

I've always wanted to have an MC clone in Python with GUI event hooks to run scripts. Like pull a lever and an email is sent sort of thing. Will this fork allow for something like that? I'm not against adding it myself if allowed, btw.

There is also ScriptCraft - https://github.com/walterhiggins/ScriptCraft Here you can hook JavaScript code into Minecraft events.

Re: Pycraft: Minecraft engine in Python

#26
post #20

I cringe when I see code like this: if len(vals) in range(1, 5): It seems like a harmless enough thing to do but that code is effectively creating a new array with values [1, 2, 3, 4, 5] and testing if the result of len() is in that array by iterating over it. This check is happening multiple times for each draw call every frame. Sadly I see this kind of stuff in Python all the time and it just adds weight to the arg…

In python 3 range() does not build a list but produce a range object. Testing x in range(a,b) will actually call the __contains__ method of the object which is O(1). The only overhead here may be the object creation. See this http://stackoverflow.com/q/30081275

It may be O(1), but the constant of a __contains__ method is almost certainly much higher than the constant of an if 1 <= len(vals) <= 5: statement.

Re: Pycraft: Minecraft engine in Python

#27
post #14

I cringe when I see code like this: if len(vals) in range(1, 5): It seems like a harmless enough thing to do but that code is effectively creating a new array with values [1, 2, 3, 4, 5] and testing if the result of len() is in that array by iterating over it. This check is happening multiple times for each draw call every frame. Sadly I see this kind of stuff in Python all the time and it just adds weight to the arg…

Why do you suspect 1 You can actually get a very good idea for what Python is doing under the hood using the dis module: dis.dis(lambda: 1 > 27 ROT_TWO 28 POP_TOP 29 RETURN_VALUE Compare to: dis.dis(lambda: len(vals) in range(1, 5)) 1 0 LOAD_GLOBAL 0 (len) 3 LOAD_GLOBAL 1 (vals) 6 CALL_FUNCTION 1 9 LOAD_GLOBAL 2 (range) 12 LOAD_CONST 1 (1) 15 LOAD_CONST 2 (5) 18 CALL_FUNCTION 2 21 COMPARE_OP 6 (in) 24 RETURN_VALUE

It may be a shorter number of bytecodes, but that does not mean it has better performance.

You have two function calls in the range version and only one in the non-range version. You are just hiding some of that code in a dynamic function call. Also, your compare_op consists of two Fewer lines of code does not mean more efficient. Regardless of whether it is source code or bytecode.

Re: Pycraft: Minecraft engine in Python

#28
post #18

Earlier quoted context omitted.

To be pedantic (which I think is warranted here), range does not return a generator, it returns a sequence called a range object. This object can be indexed, sliced, and (relevant to this discussion) supports the 'in' operator. "x in range(10)" will operate in constant time and memory in Python 3. Whether it is actually more efficient than "0 $ python3 -m timeit -s 'x = 8' 'x in range(10)' 1000000 loops, best of 3: 0…

I'm certain that the call to range is significant there, but if you think of how this actually plays out, it may be doing a naive list compare. if a compare operation is your limiting operation, then the former has to do nine operations (Is x == 0? Is x == 1? ... Is x == 8?) vs precisely two in the latter (is x >= 0? Is x From a formal CS perspective, that's why this is wrong; It's because 'in' is an o(n) operation,…

Mainly, the call to range() has to construct something that it returns, that means allocating memory, which is probably going to dwarf any other code involved in this. (In case of range(1,5) I tend to assume that constructing [1,2,3,4] is probably marginally faster than constructing rangeobject)

Also, as noted in other comments, range object has special cased O(1) implementation of in for integers (range_contains_long() in Objects/rangeobject.c)

CPython bytecode interpreter tries to minimize amount of branching it causes by using some non-obvious tricks, but still by definition it is going to cause at least one essentially unpredictable branch per interpreted instruction, so optimizing for number of branches in user python code is mostly pointless endeavor.

Re: Pycraft: Minecraft engine in Python

#29
post #10
post #8

Earlier quoted context omitted.

Yes, you are correct. But normal MC is not open source. :)

Even though Notch was supposed to make it open source at some point. Another broken promise.

What he actually said was "Once sales start dying and a minimum time has passed, I will release the game source code as some kind of open source." [0]. Sales haven't died down and he doesn't own it anymore anyway.

[0] http://web.archive.org/web/20100301103851/http://www.minecra...

Re: Pycraft: Minecraft engine in Python

#30
post #27
post #14

Earlier quoted context omitted.

Why do you suspect 1 You can actually get a very good idea for what Python is doing under the hood using the dis module: dis.dis(lambda: 1 > 27 ROT_TWO 28 POP_TOP 29 RETURN_VALUE Compare to: dis.dis(lambda: len(vals) in range(1, 5)) 1 0 LOAD_GLOBAL 0 (len) 3 LOAD_GLOBAL 1 (vals) 6 CALL_FUNCTION 1 9 LOAD_GLOBAL 2 (range) 12 LOAD_CONST 1 (1) 15 LOAD_CONST 2 (5) 18 CALL_FUNCTION 2 21 COMPARE_OP 6 (in) 24 RETURN_VALUE

It may be a shorter number of bytecodes, but that does not mean it has better performance. You have two function calls in the range version and only one in the non-range version. You are just hiding some of that code in a dynamic function call. Also, your compare_op consists of two Fewer lines of code does not mean more efficient. Regardless of whether it is source code or bytecode.

There's no argument made in the comment you replied to. Certainly not a strident one in favor of range.

It appears to me they went ahead and posted the bytecode for both because they had spun up an interpreter to get the bytecode for one.

Post reply on HN