妨碍开发人员获得高性能的三种行为!

 Alexander Garcia S. (Intel)

https://software.intel.com/zh-cn/articles/good-performance-three-developers-behaviors-that-prevent-it?utm_source=CSDN.com&utm_medium=Syndication&utm_campaign=Android_PRC_Q2-16



简介

性能是一款应用最重要的非功能性要求之一。 如果您正在阅读这篇文章,那么您可能正在使用一款应用,如浏览器或文件阅读器,所以您应该了解性能的重要性。 在本文中,我们将讨论应用的高性能以及开发人员无法实现高性能的三种行为。

第一种行为:缺乏对开发技术的理解

无论您是刚毕业,还是已经有了多年的开发经验,当进行开发时,您需要先了解一下已经开发的产品。 最好使用的是相同的编程语言。

这不是一件坏事情。 事实上,这么做通常会加快开发的速度。 但是,另一方面,它也会妨碍您学到东西。因为这种方法很少涉及到花费时间检查代码,以及理解算法和每行代码的内在运作方式。

这是开发人员可能会有的第一种行为的一个示例。 除此之外,还有其他的方式。 比如,我年轻的时候,刚开始着手软件开发,我当时的老板是我的榜样,他无论做什么都能做到最好。我在进行任何开发时,都会学习他的经验并尽可能地复制。 很多时候我都不太理解为什么他的方法可行,但是谁在乎呢,对吧? 反正它可行!

有一种开发人员,我称其为 “4x4”。 他或她无论被要求做什么,都会尽其所能地去完成。 他们经常寻找构建模块或已经完成的代码片段,将其组合在一起便作罢。 事情完成了! 这种开发人员很少花费时间理解其发现的所有片段,不去考虑或调查可扩展性、可维护性或性能。

还有一种情况会导致开发人员不理解代码的实际运作方式:从来没遇到过任何问题!当首次使用一种技术时,如果遇到问题,您就会深入了解技术细节,最后便会了解其运作方式。

就这一点,我们来看一些示例,了解一下理解技术和仅使用技术之间的区别。 大多数情况下,我是一个 .NET* web 开发人员,我会把大部分精力放在这上面。

JavaScript* 和文档对象模型 (DOM)

我们来看一下下面的代码片段。 相当普通。 代码刚刚在 DOM 中更新了一个元素的风格。 问题(这在现代浏览器中不再是问题,在此处引用是为了解释该要点)是它在 DOM 树之间来回使用了三次。 如果代码多次重复,而文档较大且复杂,将会影响应用的性能。

解决这样的问题非常简单。 我们来看看下面的代码片段。 在处理项目前,变量 myField 中存放了一个直接引用。该新代码不太长,能够更快地阅读和理解,并且具备更高的性能,因为只有一个 DOM 树接口。

我们来看一下另一个例子。 该示例来源如下:http://code.tutsplus.com/tutorials/10-ways-to-instantly-increase-your-jquery-performance--net-5551

在下图中,有两个意义相同的代码片段。 每个代码可创建一千个列表项 li 元素。 右侧的代码可为每个 li 元素添加一个 id 属性,但是左侧的代码可向每个 li 元素添加一个 class 属性。

如您所见,每个代码片段的第二部分将访问所创建的每个 li 元素(共一千个)。 我在 Internet Explorer* 10 和 Chrome* 48 中所进行的基准测试中,左侧代码花费的平均时间是 57 毫秒,右侧代码是 9 毫秒,右侧明显更少。 在此案例中,当使用不同的方式访问元素时,区别非常大。

在借用该示例时应非常小心! 该示例中有许多其他的内容需要理解,否则便会出错,比如评估选择器的顺序(从右到左)。 如果您使用 jQuery*,那么也需要了解一下 DOM 环境。 对于常规的 CSS 选择器性能概念,请参见以下文章:https://smacss.com/book/selectors

接下来我们在 JavaScript 代码中举最后一个例子。 这个例子与内存的关系更大,但是可以帮助大家理解运行方式。 浏览器中的高内存消耗也会导致出现性能问题。

下一张图片展示了使用两个属性和一种方法创建一个对象的两种方法。 在左侧,类的构造函数向对象中添加两个属性,然后再通过类的原型添加一种方法。 在右侧,构造函数同时添加属性和方法。

创建对象后,将有一千个对象使用两种技术创建。 如果比较对象使用的内存,您将会发现在 Chrome 中两种方法在 Shallow Size 和 Retained Size 中的内存使用区别。 原型方法在 Shallow Size 中使用的内存少 20%(20 Kb VS 24 Kb),在 Retained Memory 中引用的参数少 66%(20 Kb VS 60 Kb)。

如欲更好地了解 Shallow Size 和 Retained Size 内存如何操作,请参见:

https://developers.google.com/web/tools/chrome-devtools/profile/memory-problems/memory-101?hl=en

通过了解如何使用技术,您可以创建对象。 但是了解技术如何运行可帮助您从内存管理和性能方面来改进应用。

LINQ

我在准备该主题的会议演示时,想要为大家提供一个服务器端代码的示例。 我决定使用 LINQ*,因为 LINQ 已经成为 .NET 环境进行开发的直接工具,并且是最有可能提高性能的领域。

思考一下这一常见场景。 在以下图片中,有两个功能对等的代码片段。 代码的目的是列出学校的所有系以及每个系的所有课程。 在标题为 Select N+1 的代码中,我们列出了所有系,并针对每个系列出其课程。 这表示,如果有 100 个系,我们将需要向数据库中执行 1+100 次调用。

有许多方法可以解决该问题。 右图中展示了一种简单的方法。 通过使用 Include 方法(在本案例中,为便于理解,我使用的是硬编码字符串),只需调用一次数据库便可调出所有的系和每个系的课程。 在本案例中,当执行第二个 foreach 循环时,每个系的所有课程集合已经保存到内存中。

通过避免 Select N+1 问题,

我们来看一个不太明显的示例。

在下方的图片中,两个代码片段之间只有一个区别:第二行目标列表的数据类型。 您可能会问,目标类型能带来什么区别? 当您了解技术的运行方式时,便会认识到,目标数据类型可定义查询在数据库中执行的确切时刻, 进而定义每个查询的过滤器何时应用。

在 Code #1 示例中,采用了 IEnumerable,那么查询将会在 Take<Employee>(10) 执行前先执行。 这意味着,如果有 1,000 个员工,那么将会从数据库中对所有员工进行检索,然后只采用 10 条记录。

在 Code #2 示例中,查询会在 Take<Employee>(10) 执行之后再执行。 即,仅从数据库中检索 10 条记录。

以下链接中的文章深入解释了使用多种集合的区别。

http://www.codeproject.com/Articles/832189/List-vs-IEnumerable-vs-IQueryable-vs-ICollection-v

SQL Server*

在 SQL 中,如要从数据库中获取最佳性能,有许多概念需要了解。 SQL Server 非常复杂,因为它要求了解数据是如何使用的,哪些表格查询的最频繁,是被哪些字段查询的。

但是,您仍然可以通过应用一些常见的概念来改进性能,如:

  • 群集索引和非群集索引
  • 适当排列的 JOIN
  • 了解何时使用 #temp 表和变量表
  • 使用视图 vs 索引视图
  • 使用预编译的声明

简洁起见,我不会提供特定的使用案例,但是这些类型的概念是您可以使用、了解并进行充分利用的。

思维模式转变

开发人员必须要转变哪些思维模式来避免第一种行为?

  • 不要再有“我是前端或后端开发人员!”的思维模式 您可能是一位工程技术人员,并且可能成为一个领域的专家,但是请不要将其当作不学习其他领域的借口。
  • 不要再有“让专家来做吧,他们更快!”的思维模式 在当今世界,到处都需要灵活性,我们必须成为可互换的资源,必须学习不擅长的领域。
  • 不要在对自己说“我不知道!” 当然! 如果容易,那么我们就都成专家了! 花点时间去阅读、提问和理解。 这不容易,但是能够带来回报。
  • 不要再说“我没时间!” 我明白这一点。 确实如此。 但是曾经有一位英特尔同事告诉我:“如果对一件事情充满激情,那么带宽就是无限的。” 我就是这样,在周六的上午 12:00 写下了这篇文章!

第二种行为:对于某些技术存在偏见

我自 1.0 版开始便开始在 .NET 中开发。 我知道 Web Forms 运行的每个小细节,以及许多 .NET 客户端库(我对一些进行了定制)。 当我看到市场上推出“模型图形控制器 (MVC)”时,我并没有使用它,因为“我们并不需要它”。

对于我最初不喜欢而后来又广泛使用的特性,我不再一一列举。 但是这可以表明我的观点,即人们对于使用某些技术存在偏见,从而无法获得更高的性能。

我经常听到的一种争论,究竟在查询数据时使用实体框架 (Entity Framework) 中的 LINQ-to-Entities 还是 SQL 存储过程。 人们经常惯于使用其中一种,并且在其他开发中继续沿用。

让人们对某项技术产生偏见的另一个原因是其是否喜欢开源方式。 这会让人们不去思考哪种方式最适合其当下的情况,而是选择最符合其自身理念的方式。

有时,外部因素(如截止日期)会督促我们做决定。 为了选择最适合我们应用的技术,我们需要时间去阅读、操作、比较并得出结论。 当我们开发新产品或现有产品的新版本时,经常会遇到来不及的情况。 两种方法可以解决这一情况:要求延长所需的时间,或者加班学习相关知识。

思维模式转变

开发人员必须要转变哪些思维模式来避免第二种行为:

  • 不要再说“这种方法一直可行”、“我们一直使用这种方法”等。 我们需要找出并使用其他的选项,尤其如果有数据支持这些选项。
  • 不要再修复解决方案! 有时,人们会想使用无法提供预期效果的技术。 然后他们会花费大量的时间去修复该技术。 此处,他们是“修复解决方案”,而不是把注意力放在问题上,否则他们可能会找到其他更快、更高效的解决方案。
  • “我没时间!” 当然,我们没有时间学习或尝试新事物。 我明白这一点。

第三种行为:不了解应用的基础设施

在花费了很大的力气创建了一个完美的应用之后,现在要部署它了! 我们对所有功能都进行了测试。 所有功能在我们的设备中都能够完美运行。 10 个测试人员对应用及其性能都非常满意。 还有什么会出错呢?

什么都有可能出错!

您是否问过自己下面的问题?

  • 我是否将应用设定在负载均衡的环境中运行?
  • 我是否准备将应用托管到包含许多实例的云中?
  • 我的目标产品设备中还有多少个其他的应用运行?
  • 该服务器上还运行了何种其他应用? SQL Server? 报告服务? 一些 SharePoint* 扩展?
  • 我的最终用户位于何地? 他们是否遍布全球各地?
  • 我的应用在未来五年将会拥有多少最终用户?

我了解并非所有的问题都与基础设施相关,但是请耐心听我继续讲。 通常,我们的应用最后运行的情形与我们中间阶段的服务器不同。

我们来看一些可能会对我们应用的性能产生影响的情形。 我们首先看一下全球用户。 可能我们应用的速度非常快,我们没有听到过美国客户的抱怨,但是我们马来西亚客户的速度却并非如此。

这种情况有许多解决办法。 其一,我们可以使用内容交付网络 (CDN),存放静态文件,那么从其他地点加载页面的速度就会提高。 下面的图片展示了我所说的方法。

我们再举一个可能会发生的情况,比如同时运行 SQL Server 和 Web Server 的服务器上运行的应用。 在这种情况下,我们在同一台设备上使用了 2 台 CPU 密集型服务器。 我们如何解决这一问题? 仍然假定是在互联网信息服务 (IIS) 服务器上运行 .NET 应用,我们可以利用 CPU 关联。 CPU 关联可将一个制程与机器上的一个或多个特定内核绑定。

例如,我们在一台包含 4 个 CPU 的设备中使用了 SQL Server 和 Web Server (IIS)。

如果我们让操作系统来决定哪些 CPU 使用 IIS 或 SQL Server,则需要进行各种设置。 我们可以向每台服务器指派 2 个 CPU。

或者我们可以将所有的处理器都指派给 1 台服务器。

在这种情况下,我们可能就会陷入僵局,因为 IIS 可能会参与太多请求,它们会占用所有的处理器,可能有一些需要访问 SQL,当然这不会发生。 无可否认,这不可能发生,但是能解释我的观点。

还有一种情形:一个制程不会同时在相同的 CPU 上运行。 其中有太多环境转换。 环境转换会导致服务器的性能降级,进而影响该服务器上运行的应用。

尽可能减少影响的一种方法是在 IIS 和 SQL 上使用处理器关联。 这样,我们便可以确定 SQL Server 和 IIS 需要多少颗处理器。 这可以通过更改 IIS 的 CPU 类别中的处理器关联设置和 SQL server 数据库中的“关联掩码”来实现。 以下图片展示了上述两种情况。


此外,还可以从基础设施层面上采取一些方法来提高应用的性能,如使用 Web Gardens 和 Web Farms。

思维模式转变

开发人员必须如何转变思维模式来避免第三种行为?

  • 不要再有“这不是我的工作!”的思维模式 作为工程师,我们必须尽可能地拓展我们的知识,才能够为我们的客户提供最完整的卓越解决方案。
  • “我没时间!” 当然,我们总是没有时间。 这是所有人都要转变的思维模式。 学会创造时间是专业人士取得成功,超越对手,脱颖而出的关键!

不要感到内疚!

千万不要感到内疚! 这不是您的问题! 真的,我们没时间! 我们有家庭,有自己的爱好,还需要休息!

关键是要意识到,有时候,除了写好代码之外,还有许多方法可以提高性能。 我们可能已经有过或未来将会有上述部分或全部行为。

我来为大家提供一些建议,帮助大家规避这些行为。

  1. 创造时间。 当您的项目需要进行评估时,确保您进行了全面研究和测试之后,再得出结论并制定决策。
  2. 请尝试按照个人测试应用的方式来创建。 借助这种应用,您可以避免开发时需要在应用中尝试新方法。 这个错误我们在某些时候都会犯。
  3. 寻找已经了解相关知识的伙伴,与其结对进行编程。 在处理基础设施的同事部署应用时,与其合作部署。 这值得您花费时间来做。
  4. Stack Overflow 非常可怕!!! 事实上,我在该社区为大家提供帮助,我相当大一部分问题也从那里得到解决。 但是,如果您用它来“复制和粘贴”答案,那么您就不会得到完整的解决方案。
  5. 不要再做前端人员。 也不要再做后端人员。 请成为一个主题的专家(如果您想的话),但是请在谈论您不擅长的领域时保持谦虚。
  6. 帮助别人! 这可能是学习的最佳方式。 当您帮助别人解决其问题时,您以后就不会遇到相同或相似的状况,从长远角度来看便可节省时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值