Differences in PyPy generated gcode (Updated!)

Comparison of CPython and PyPy fill patterns

As I said in my last post, the gcode generated when using PyPy instead of CPython can differ, so I’ve been trying to find out when. I already knew that the standard 20mm calibration cube had no differences, and the same seems to be true about a simple cylinder generated with OpenSCAD. So next, I generated a cylinder intersected a box and the differences appeared. It’s in the picture below, but here is the code:

$fn = 25; # So the cylinder is reasonably smooth

union() {
	cylinder(30, 12, 12);

	translate([5, -5, 25]) {
		rotate([0, 30, 30]) {
			cube([35, 35, 5], true);
		}
	}
}

So what’s different?

The first thing I noticed when running a diff between the files is that the bounding boxes for layers can be defined in a slightly different order. For example while CPython will generate this:

(<boundaryPoint> X26.813 Y-12.614 Z16.375 </boundaryPoint>)
(<boundaryPoint> X9.313 Y17.697 Z16.375 </boundaryPoint>)
(<boundaryPoint> X5.232 Y15.341 Z16.375 </boundaryPoint>)
(<boundaryPoint> X22.732 Y-14.969 Z16.375 </boundaryPoint>)

PyPy will put the first line as the fourth. It’s the same bounding box, but it shows up in a diff. More importantly, it also makes different decisions when it comes to fill. My profile is set to a 20% fill, and you can see the differences in the image above. Neither implementation’s output seems to be actually suffer. In the case above (a few layers after the intersection starts), PyPy uses fewer straight lines. But further up on the object CPython put more angles in. Things are never too different though, they are kind of close. Maybe this isn’t something to worry too much about.  I’ll have to spot check one of the more complicated objects to see if an obvious problem occurs.

Update:

After posting this I received this email from Carl Friedrich Bolz, one of the PyPy developers:

[I] wanted to point out two of the most likely sources of differences (I’m a PyPy developer):

– dictionary order is not guaranteed between implementations
– CPython probably uses x87 floating point math, whereas PyPy uses SSE2. those two have slightly different rounding behavior, so if the algorithms are not numerically stable, you can get diverging results.

if you find out that it’s not one of those too, it might be a bug in PyPy.

The SSE issue was actually one of my theories for what was going on. Rounding on floating point numbers would explain what Skeinforge is doing. On my example layer above, PyPy must have thought it was a hair under on plastic so it added some extra kinks in the fill lines to make up the difference. The dictionary ordering explains the outline coordinates.

I haven’t gotten around to spot checking the larger object, but between my results above and confirmation that PyPy handles floating point number differently… I think I’m going to start using it permanently with Skeinforge. The 4x speedup is really wonderful.

2 thoughts on “Differences in PyPy generated gcode (Updated!)

    • That’s true, but I haven’t been willing to go that far to test it. I’ve just been using the stock OS X 10.7 Python binary, which I’m guessing is only compiled to use the x87 instructions.

Comments are closed.