On 5/30/16 11:46 AM, Andrei Alexandrescu wrote:
On 05/30/2016 11:22 AM, Steven Schveighoffer wrote:
On Monday, 30 May 2016 at 15:12:41 UTC, Andrei Alexandrescu wrote:
On 05/30/2016 09:30 AM, Steven Schveighoffer wrote:
My iopipe library is 2x as fast.

Cool! What is the trick to the speedup? -- Andrei

Direct buffer access and not using FILE * as a base.

So what primitives do you use? The lower-level descriptor-based
primitives open() and friends? http://linux.die.net/man/2/open

Yes. That is not the focus of my library, though. I have a very crude and basic I/O type that just wraps the system calls into a nice API.

What do you mean by direct buffer access?

I mean instead of pulling data out one character at a time from another buffer into your buffer, you have direct access, using all the speedups that CPUs provide when accessing arrays.

I'm also allowing the compiler to inline as much as possible because all the code is available.

What is the relative contributions of these (and possibly other) design
decisions to the overall speed?

I don't know. I started with a vastly different design and it ended up being twice as fast. So it's difficult to say for certain what are the major factor that is slowing down std.stdio.

At DConf, Adrian Matoga presented his very similar flod library, and I was surprised when it was faster than mine at processing lines, even though he was still using std.stdio.File. So I found a few reasons why mine was slower and fixed them. The things that made it much faster were:

1. Switched to direct pointer access (i.e. doing while(*ptr++ != lineTerminator) {} ) when the buffer type was an array. 2. I was stupidly accessing the member of the struct which held the terminator instead of allocating a stack variable for it. After doing that (and making it immutable), the compiler was much better informed on how to optimize.

There may even be more opportunities for speedup, but I'm pretty satisfied with it at the moment.

How can we integrate some of these in std.stdio without breaking
backward compatibility,

There are 2 main issues with FILE *:

1) it does not provide buffer access, so you must rely on things like getline if they exist. But these have their own problems (i.e. do not support unicode, require C-malloc'd buffer)

2) All calls are opaque, so no inlining can occur. This is especially painful if you have to call fgetc in a tight loop. This goes back to the buffer access issue -- FILE * only lets you look ahead one character.

So I think the only real way to provide a decent performance improvement without breaking C compatibility is to write code that is aware of the implementation details of the opaque FILE * type. Just like getline.

or offer additional artifacts that integrate
seamlessly and don't need to be compatible?

I had a grand plan to replace stdio with something that "evolves" to faster D based i/o when you did D-like things. But it's very complex, and each addition of code to stdio makes things more complex.

In reality, I think we only need to be C compatible for stdout (possibly only via write[f][ln]), and possibly stdin. There's no reason we need to use C i/o for any other uses. What makes things difficult is that everything in Phobos uses std.stdio.File for all i/o and that uses C i/o.

-Steve

Reply via email to