Show HN: Tokamak – Server-side framework for Zig
github.com
github.com
But, it's very slow. A quick test shows Sinatra is about 2x faster. Maybe I'm in the minority, but I feel that a primary reason to give up a GC is for performance.
I believe you _can_ get keepalive working with `std.http.Server`, but it seems like Tokamak isn't using it that way. The implementation is a thread-per-connection. Those are the two most obvious issues specific to this implementation.
But I believe the bulk of the issues relate to `std.http.Server`. It shouldn't be public/used.
TBH perf was not my priority yet, I only wanted predictable memory usage and Zig was perfect choice for that (when compared to node.js).
This server assumes *all* clients are well behaved and standard compliant; it can and will deadlock if a client holds a connection open without sending a request.
Atop your readme, you point out that nginx or another reverse proxy should be used. Kudos for that.As for performance, I'd be curious what gains you get using `std.http.Server` with keepalive and a threadpool. Possibly you can re-use your ThreadContext - having 1 per thread in the threadpool that you can re-using. `std.Thread.Pool` is also very poorly tuned for a large number of small batch jobs, but that's a place to start.
[1] https://github.com/ziglang/zig/blob/b3aed4e2c8b4d48b8b12f606...
I'm wondering where exactly is the problem in the std.http.Server because it might be easy to fix. Maybe it's just that nobody cared yet... But 2x slower than Ruby sounds awful.
> Possibly you can re-use your ThreadContext
Yes, this is the idea, and extending the injector for request-scoped and thread-scoped dependencies.
There must be something very wrong
[1]: Rust also has a focus on low-level programming, but has many higher level constructs / a more complicated type system so I'm not too surprised when I see nice web frameworks in that ecosystem
- zig has powerful meta-programming, it's a bit like functional programming with structs. I would consider that high-level concept, at least comparable to traits and constraints
- zig has anytype duck-typing. in any function, you can say that some arg is anytype, and then you can pass anything to it. type-checking still works, because it's done when you actually instantiate the function with known type. this is great for flat and simple API surface.
- server-side code usually have short life-time (request), so you can just use arena for all allocations, and free everything when you finish, this is super-simple to do in zig, because every api accepts allocator if it needs to allocate. rust arenas have lifetimes, which makes them problematic to use/embed deep down in the hiearchy. zig is not aiming for 100% safety so this is easy.
Is this any different from generics? Rust lets you define arguments with `impl Trait` as a shorthand for a generic type `T: Trait`, but this sounds pretty similar to just defining a function over a generic type `T`, albeit with different syntax.
Zig is doing just fine without any trait mechanism and it simplifies the language a lot but it does come up from time to time. The usual solution is to just get type information via @typeInfo and error out if the type is something you're not expecting [0]. Not everybody is happy about it though [1] because, among other things, it makes it more difficult to discover what the required type actually is.
[0] https://github.com/ziglang/zig/blob/b3aed4e2c8b4d48b8b12f606...
Depends on the way generics is implemented in the language you're talking about.
In D, for example, Zig's `anytype` is equivalent to using a template type `T` in D without any constraints. The result is the same: the implementation can call any method, but it must exist when the type is instantiated (on an invocation).
Example in D:
https://run.dlang.io/?compiler=dmd&args=-unittest&source=str...