我对我对软件开发的兴趣充满热情,特别是人体工程学创建的软件系统的难题,可以解决最广泛的问题,同时使尽可能少的妥协。我还喜欢将自己视为系统开发人员,通过安德鲁·凯利(Andrew Kelley)的定义,这意味着有兴趣完全了解他们正在使用的系统的开发人员。在此博客中,我与您分享有关解决以下问题的想法:构建一个可靠且性能的全堆栈企业应用程序。一个挑战,不是吗?在博客中,我专注于“ Performant Web服务器”部分 - 我觉得我可以提供新的视角,因为其余的要么是好事,要么没有添加。
一个主要的警告 - 将有没有代码示例,我实际上尚未对此进行测试。是的,这是一个主要缺陷,但实际上实施这将需要很多时间,我没有,并且在发布有缺陷的博客并且根本不发布博客之间,我坚持使用前者。您已被警告。 [2
以及我们将从哪些部分组装出我们的应用程序?
与Linux内核密切集成的ZIG Web服务器。这是表演部分,我将在此博客中重点关注。
保持,请首先让我们的系统程序员戴上帽子。 Coroutines不是银弹,没有什么。实际的好处和缺点是什么?
他们更好地与用户空间调度程序合作。由于内核调度程序是先发制人的,因此线程执行的任务是分配的时间切片。如果实际任务不适合切片 - 某些CPU时间会浪费。与goroutines相反,与goroutines相适应了不同的goroutines执行的许多微任务,并尽可能地融入OS-Thread的同一时间。
[2
例如,GO运行时将goroutines多重在OS线程上。线程共享页面表,以及流程拥有的其他资源。如果我们将CPU隔离和对混合物的亲和力引入 - 线程将不断在其各自的CPU内核上运行,所有的OS数据结构都将保持在内存中,而无需交换,则用户空间调度程序将使用CPU时间分配精确的CPU时间,因为它使用协作多任务模型。竞争甚至可能吗?通过旁观线程的OS级抽象,并用Goroutine的OS级抽象来实现性能。但是翻译中没有丢失?
如果我们使用单个过程的模型和许多Coroutines进行独立任务,那么当一个Coroutine超越内存限制时 - 由于在过程级别跟踪内存使用情况,因此整个过程被杀死。最好的情况是 - 如果您使用cgroup(在库伯内特的豆荚中自动是这种情况,每个豆荚都有一个cgroup) - 整个cgroup被杀死。建立可靠的系统需要考虑到这一点。那CPU时间呢?如果我们的服务同时被许多计算密集的请求遭到打击,则它将变得无反应。然后截止日期,取消,重新恢复,重新启动。
并发模型
由于对资源的使用进行了跟踪,因此理想情况下,我们将为每个可预测的执行单元产生一个新的过程。然后,我们为CPU时间和内存设置了ULIMIT - 我们很高兴! Ulimit具有软限制,这将使该过程在击中软限制时优雅地终止,如果不发生这种情况,可能是由于错误造成的 - 在击中硬限制时会有力终止。不幸的是,在Linux上产卵的新过程很慢,对于许多Web框架以及其他系统(例如时间),每个请求都不支持每个请求产生新的过程。此外,过程切换更昂贵 - 牛和CPU固定可以减轻,但仍然不理想。不幸的是,长期运行的过程是不可避免的现实。 [2
但是,与每次重新要求的方法相比,性能仍然会受到影响。首先,在过程内存表周围复制很昂贵。由于该表包含对内存页面的引用,因此我们可以使用大型页面,从而限制要复制的数据大小。只有低级语言(例如Zig)才能直接使用。此外,操作系统级别的多任务是先发制人的,而不是合作的,这总是效率较低。还是?
与Linux合作多任务处理
有SYSCALL SCANE_YIELD,它允许线程在完成其工作的一部分时放弃CPU。似乎很合作。是否有一种方法可以要求给定尺寸的时间切片?实际上,有 - 带有调度策略sched_deadline。这是一个实时策略,这意味着对于请求的CPU时间切片,该线程不间断地运行。但是,如果切片被超支 - 先发制人会启动,并且您的线程被换成并剥夺了。而且,如果切片不足 - 线程可以调用Sched_yield以提早发出信号,从而可以运行其他线程。这看起来像是两全其美的 - 合作和优势模型。
[2
这两种策略都达到了与Coroutine模型 -
通过与内核合作,可以使应用程序任务使用最小的中断。
Python作为嵌入式脚本语言这全是用于高性能,低延迟,低级侧面的,锯齿闪闪发光的地方。但是,当涉及到应用程序的实际业务时,灵活性比延迟更有价值。如果一个过程涉及真实的人在文档上签字 - 计算机的延迟可以忽略不计。同样,尽管性能遭受了损失,但面向对象的语言为开发人员提供了更好的原语,以模拟与业务的领域建模。在最远的一端,诸如Flowable和Camunda之类的系统允许管理人员和运营人员可以更灵活地编程业务逻辑,并具有较低的入境障碍。像Zig这样的语言不会对此有所帮助,只能阻碍您的方式。
的确,我们可以,因为两者都支持C ABI。我们可以让python解释器从ZIG过程中运行,而不是作为一个单独的过程,从而减少了运行时成本和胶水代码的开销。这进一步使我们能够在Python中使用Zig的自定义分配器 - 设置用于处理单个请求的竞技场,从而减少垃圾收集器的开销,并设置内存帽。一个主要的限制是垃圾收集和IO的Cpython运行时产卵线,但我没有发现证据表明它确实如此。我们可以通过使用AbstractMemoryloop中的“上下文”字段将曲调挂在Zig的自定义事件循环中,并通过每个曲线记忆跟踪。可能性是无限的。
结论
进一步阅读
https://eli.thegreenplace.net/2018/measuring-context-switching-and-memory-overheads-for-linux-threads/
免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。
Copyright© 2022 湘ICP备2022001581号-3