每个人都知道异步为您提供“更好的吞吐量”、“可扩展性”以及在资源消耗方面更高效。在进行下面的实验之前,我也想到了这种(简单的)方式。它基本上表明,如果我们考虑异步代码的所有开销并将其与正确配置的同步代码进行比较,它几乎不会产生性能/吞吐量/资源消耗优势。
问题:与正确配置线程池的同步代码相比,异步代码实际上执行得更好吗?可能是我的性能测试存在某种戏剧性的缺陷?
测试设置:两个 ASP.NET Web API 方法,JMeter 尝试使用 200 个线程线程组调用它们(30 秒恢复时间)。
[HttpGet]
[Route("async")]
public async Task<string> AsyncTest()
{
await Task.Delay(_delayMs);
return "ok";
}
[HttpGet]
[Route("sync")]
public string SyncTest()
{
Thread.Sleep(_delayMs);
return "ok";
}
这是响应时间(对数刻度)。请注意,当线程池注入(inject)足够多的线程时,同步代码如何变得更快。如果我们要预先设置线程池(通过 SetMinThreads),它从一开始就会优于 async。
您会问的资源消耗情况如何。 “线程在 CPU 时间调度、上下文切换和 RAM 占用方面的成本很高”。没那么快。线程调度和上下文切换是高效的。就堆栈使用而言,线程不会立即消耗 RAM,而只是保留虚拟地址空间并仅提交 tiny fraction。这是实际需要的。
让我们看看数据怎么说。即使线程数量更多,同步版本也具有更小的内存占用(映射到物理内存的工作集)。
更新。我想发布后续实验的结果,它应该更具代表性,因为避免了第一个实验的一些偏差。
首先,第一个实验的结果是使用 IIS Express 获取的,它基本上是开发时间服务器,所以我需要远离它。此外,考虑到反馈,我已经将负载生成机与服务器(同一网络中的两个 Azure VM)隔离开来。我还发现一些 IIS 线程限制是 from hard to impossible违反并最终切换到 ASP.NET WebAPI 自托管以从变量中消除 IIS。请注意,此测试的内存占用/CPU 时间完全不同,请不要比较不同测试运行中的数字,因为设置完全不同(托管、硬件、机器设置)。此外,当我转移到另一台机器和另一个托管解决方案时,线程池策略发生了变化(它是动态的)并且注入(inject)率增加了。
设置:延迟 100 毫秒,200 个 JMeter“用户”,30 秒加速时间。
我想用以下内容来结束这些实验:是的,在一些特定的情况下 (更像实验室)情况下,同步与异步可以获得可比较的结果,但在工作量不能 100% 可预测且工作量不均匀的现实世界中,我们不可避免地会遇到某种线程限制:服务器端限制,或线程池增长限制(请记住,线程池管理是自动机制,其属性并不总是容易预测)。此外,同步版本确实有更大的内存占用(工作集和更大的虚拟内存大小)。就 CPU 消耗而言,异步也胜出(每个请求的 CPU 时间指标)。
在具有默认 设置的 IIS 上,情况更加引人注目:由于对线程数的严格限制,同步版本的速度慢了一个数量级(吞吐量也更小)- 20 per CPU .
PS.一定要为 IO 使用异步管道! [...松了一口气...]
最佳答案
Everybody knows that asynchrony gives you "better throughput", "scalability", and more efficient in terms of resources consumption.
可扩展性,是的。吞吐量:视情况而定。每个异步请求都比等效的同步请求慢,因此只有当可扩展性发挥作用时,您才会看到吞吐量优势(即,请求数多于可用线程数)。
Does asynchronous code actually perform so much better comparing to the synchronous code with correctly configured thread pool?
好吧,要注意的是“正确配置的线程池”。您假设您可以 1) 预测您的负载,以及 2) 有一个足够大的服务器来处理每个请求使用一个线程。对于许多(大多数?)现实世界的生产场景,这两个或其中一个都不成立。
来 self 的 article on async ASP.NET :
Why not just increase the size of the thread pool [instead of using async]? The answer is twofold: Asynchronous code scales both further and faster than blocking thread pool threads.
首先,异步代码比同步代码扩展得更远。使用更实际的示例代码,ASP.NET 服务器(经过压力测试)的总 可伸缩性呈现成倍增长。换句话说,异步服务器可以处理数倍于同步服务器的连续请求数(两个线程池都已调至该硬件的最大值)。但是,这些实验(不是我做的)是在平均 ASP.NET 应用程序的预期“现实基线”上完成的。我不知道如何将相同的结果转移到 noop 字符串返回。
其次,异步代码比同步代码扩展得更快。这个很明显;同步代码可以很好地扩展到线程池线程的数量,但扩展速度不能快于线程注入(inject)率。因此,如响应时间图表开头所示,您对突然重载的响应非常缓慢。
我觉得你所做的工作很有趣;我对内存使用差异(或者更确切地说,没有差异)感到特别惊讶。我很乐意看到你把它写成一篇博文。建议:
作为最后的提醒(也来 self 的文章):
Bear in mind that asynchronous code does not replace the thread pool. This isn’t thread pool or asynchronous code; it’s thread pool and asynchronous code. Asynchronous code allows your application to make optimum use of the thread pool. It takes the existing thread pool and turns it up to 11.
关于c# - 相对于正确配置的同步模型,异步模型真的能提高吞吐量吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55823184/
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何
我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss
我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢
我有一些非常大的模型,我必须将它们迁移到最新版本的Rails。这些模型有相当多的验证(User有大约50个验证)。是否可以将所有这些验证移动到另一个文件中?说app/models/validations/user_validations.rb。如果可以,有人可以提供示例吗? 最佳答案 您可以为此使用关注点:#app/models/validations/user_validations.rbrequire'active_support/concern'moduleUserValidationsextendActiveSupport:
对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs
我有一个正在构建的应用程序,我需要一个模型来创建另一个模型的实例。我希望每辆车都有4个轮胎。汽车模型classCar轮胎模型classTire但是,在make_tires内部有一个错误,如果我为Tire尝试它,则没有用于创建或新建的activerecord方法。当我检查轮胎时,它没有这些方法。我该如何补救?错误是这样的:未定义的方法'create'forActiveRecord::AttributeMethods::Serialization::Tire::Module我测试了两个环境:测试和开发,它们都因相同的错误而失败。 最佳答案
ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序