werift-webrtc: Slow data channels?

First of all, great job on this library. As a long time WebRTC and Javascript/Typescript fan I have been searching for something like this for a long time. A library with no external dependencies, amazing!

One thing I noticed is that the data channels are quite slow, at least compared to the wrtc node library. Am I doing something wrong or is this a known issue?

I ran a quick 30 second test uploading an 800mb file, it did not upload the whole file in either case (although I am on fiber here). The results are as follows

  • wrtc: 70MB in 30 seconds
  • werift: 3.4MB in 30 seconds

I wouldn’t consider the wrtc fast, although it does perform quite a bit better than werift in my quick test here.

The setup:

  • Digital Ocean Server and MacOS client (both on 1GB/s internet connections)
  • I have an “agent” on the server and a cli on my Mac, both running wrtc or werift on each side.
  • The file is on my Mac being sent to the Digital Ocean server (SFO2) and I am in Seattle, WA (not too far away)

The code:

  • To upload the file, I am sending chunks of various sizes. For the wrtc library I have to be quite careful because it crashes if the buffer fills up.
  • wrtc is the fastest when i send about 5000 byte chunks (which I have always found strange) to the datachannel
  • werift seems to like 65000 byte chunks (although 1mb chunks performed about the same)
  • I stop submitting to the buffer if it is over 16438932 bytes, and start again when the datachannel buffer drops below that number.
  • on the server side, I am monitoring the events right off the datachannel, to rule out any issues on that end. The server writes to the disk using Node stream, and based on the logs it takes a few milliseconds to write then waits again for the event to fire.

Thanks again for all the work on this library. Let me know if you do have any suggestions or if I can clarify more on the setup/code.

About this issue

Most upvoted comments

but instead I actually split the werift streams into dedicated worker threads so that they can at least be scheduled across all cores independent of the main thread

Using separate worker threads is what I do in Scrypted as well, it schedules nicer with multiple cores and multiple streams.

I don’t know the answer to your question, but my project uses werift via a dependency on another project and, since the switch to werift, I’ve struggled to determine if it is fast enough. The streams I’m processing are about 3Mbps, and it seems like it can barely keep up. Still, I think that’s better than the results you posted.

Basically, I’ve found werift to be incredibly reliable and overall compatible, but performance, that’s an entirely different story. I’ve tried to spend some time with the code and, honestly, I didn’t see a lot of places where it could be improved, but I’m not a very experienced Javascript developer and I’m in way over my head. However, the overall packet path is pretty straightforward. When I did some performance traces the biggest overhead seemed to be in the cost of decrypting SRTP using CTR encryption because it requires a call to createDecipheriv() for every packet, which just seems costly no matter what.

Summary, it’s an amazing project, I’ve learned a ton from it, but I’m just not sure if Typescript/Javascript is fast enough to really be a streaming engine for hard core use. That being said, between my project and other projects built using the dependency, there are literally 10,000+ users, so I guess that alone says it’s good enough for that case, even if just barely. On the other hand, if I needed a higher bitrate, I don’t think it would work, except perhaps on higher end hardware, although, even there, I struggle to get much performance. On Linux, increasing the UDP buffer at the OS level helped a good bit, but it’s not always possible (for example, when running in Docker or without privileges). I really wish NodeJS had native WebRTC functionality.