`

解剖一个有缺陷的微基准测试

 
阅读更多

原文:《Anatomy of a flawed microbenchmark

 

解剖一个有缺陷的微基准测试

前言

即使“良好的性能”不是一个项目的关键需求,甚至不是需求之一,你也很难忽略性能方面的考虑。因为你可能会认为不考虑性能的程序员不是好工程师。在通往编写高性能代码的过程中,开发人员经常会编写基准测试程序来测量对比不同实现方式的性能。不幸的是,正如《Dynamic compilation and performance measurement》所说,与其它静态编译语言相比,评估 Java 中一段代码或数据结构的性能要困难得多。

 

一个有缺陷的微基准测试

我十月份的文章《More flexible, scalable locking in JDK 5.0》发布后,一位同事给我发来一段基准测试代码 SyncLockTest(见下文“有缺陷的 SyncLockTest 微基准测试”),号称能测量出 synchronized 原语和新的 ReentrantLock 类两者哪个更快。在他的笔记本电脑上运行完这段测试代码后,他得出“synchronized 原语更快”这个与我文章相反的结论,而证据就是他的这次基准测试。然而他的整个基准测试过程中,微基准测试的设计、实现、执行以及对结果数据的解释都存在缺陷。这个同事是个非常聪明的人,他已经在这方面的微基准测试已经有较多经验了。这也反衬出微基准测试真的很难。

 

有缺陷的 SyncLockTest 微基准测试

interface Incrementer {
  void increment();
}

class LockIncrementer implements Incrementer {
  private long counter = 0;
  private Lock lock = new ReentrantLock();
  public void increment() {
    lock.lock();
    try {
      ++counter;
    } finally {
      lock.unlock();
    }
  }
}

class SyncIncrementer implements Incrementer {
  private long counter = 0;
  public synchronized void increment() {
    ++counter;
  }
}
 
class SyncLockTest {
  static long test(Incrementer incr) {
    long start = System.nanoTime();
    for(long i = 0; i < 10000000L; i++)
      incr.increment();
    return System.nanoTime() - start;
  }
 
  public static void main(String[] args) {
    long synchTime = test(new SyncIncrementer());
    long lockTime = test(new LockIncrementer());
    System.out.printf("synchronized: %1$10d\n", synchTime);
    System.out.printf("Lock:         %1$10d\n", lockTime);
    System.out.printf("Lock/synchronized = %1$.3f",
      (double)lockTime/(double)synchTime);
  }
}

SyncLockTest 定义了同一个接口的两种实现,并使用 System.nanoTime() 方法对每种实现的 10,000,000 次调用进行计时。这两种实现都是线程安全的计数器。其中一个使用了内建的同步(synchronized 原语),另一种使用了 ReentrantLock 类。这份基准测试代码的目标是为了回答“synchronized 原语和 ReentrantLock 类哪个更快”。让我们来分析一下为什么这份看上去没有错误的基准测试为什么无法达到它所声称的目标,或者说这份测试到底测了什么。

 

概念上的缺陷

先不谈实现上的缺陷,这份基准测试在概念上就有严重缺陷——它误解了它试图要解决的问题。它试图测量比对(内建)synchronization(synchronized 原语)和 ReentrantLock 的性能开销,也就是两者在协调多线程操作时所用技术的性能开销。但是这份测试代码只有一个线程,所以永远不会有竞争。它从一开始就遗漏了测试与锁有关(需要锁)的场景。

众所周知,在早期的 JVM 实现中,非竞争场景下的(内建)同步(synchronized 原语)比较慢。但是这性能问题现在基本上已得改善。(查看文末相关话题了解JVM优化非竞争场景下内建同步性能所使用的技术。)另一方面,竞争场景下(内建)同步的开销仍然比非竞争场景下要大得多。当发生锁竞争时,JVM 需要维护一个等待线程的队列,此外还需调用操作系统的方法对那些未能立即拿到锁的线程进行阻塞和解除阻塞操作。此外,处于高度竞争的应用往往伴随着低吞吐量,因为线程调度花费的时间更多,实际处理业务的时间少了,而且当线程被阻塞等待锁时可能导致 CPU 处于空闲状态。测试(内建)同步(synchronized 原语)的基准测试必须考虑到现实情况中的竞争程度。

 

方法上的缺陷

该基准测试设计在执行方面至少存在两处错误:

  • 它运行在单处理器系统中(注:是单线程模式)
    • 这并不是高并发程序运行的常规环境。在这样的环境中,同步操作的性能表现会和多处理器系统环境有本质上的不同。
  • 它只在一个平台上做过测试

当测试一个原语(尤其是与底层硬件交互如此重要)的性能时,在得出结论前,有必要在更多不同平台上测试。当测试并发这类复杂的场景时,建议在十个以上不同测试系统,不同处理器和不同数量的处理器场景中测试后,再得出总体的性能概论。(内存配置和处理器代次也是需要考虑的。)

 

实现上的缺陷

在实现方面,SyncLockTest 忽视了许多动态编译相关的特性。正如你在《Dynamic compilation and performance measurement》所看到的,HopSpot JVM 会先以解释模式执行一段代码(注:以方法为单位),只有当执行次数达到一定数量(阀值)时才会将其编译成机器码。如果没有正确对JVM进行预热,会在两方面严重影响性能测量。首先,JIT分析与编译代码的耗时被包含到了测试的运行时间内。更重要的是,如果编译发生在测试(正式测试代码)运行中,你的测试结果(耗时)将是部分解释运行的耗时的和,加上JIT编译耗时,加上优化后执行的耗时。这样的结果对于被测代码的真实性能没有多少有用的信息。另一方面,如果在运行(正式)测试前,代码没有被编译(编译成机器码)过,且在测试运行期间没有发生编译,那么你的整个测试过程都是解释执行的。这样的结果也无法给你多少对于被测代码在真实世界中性能的有用信息。

SyncLockTest 也受到了《Dynamic compilation and performance measurement》中所讨论的内联和反优化问题的影响。在那篇文章中,第一份耗时测量代码以单态调用转化的方式被激进地内联(优化)了。(单态调用转换是指针对虚方法的调用被转换为对目标方法的直接调用。Java中的方法默认是虚方法,是一种面向对象设计中的多态特性)第二份代码则因为随后JVM载入另一个继承自同一基类或接口的类而被“反优化”。当耗时测试方法来自一个 SyncIncrementer 实例时,(JVM)运行时识别出只有一个实现了 Incrementer 接口的类被载入,所以会将针对虚方法 increment() 的调用转换为对 SyncIncrementer 实例方法的直接调用。随后,当耗时测试方法来自一个 LockIncrementer 实例时,test() 方法被当作虚方法重新编译。这意味着第二份 test() 方法耗时测试中的每个迭代比第一份做了更多的工作。这好比我们是在比较苹果与橘子的区别。这会严重扭曲测试结果,导致无论哪种实现,第一个先运行的方案会显得更快。

 

基准测试代码看上去不像真实的代码

到目前为止所讨论的基准测试代码缺陷可以通过合理的返工修复。如,引入类似“竞争程度”这样的测试参数,并且在大量不同的系统上、不同测试参数值条件下测试。但是还有一些缺陷可能无法通过任何调整来解决。为了了解为什么会这样,你需要像JVM一样思考,并了解当 SyncLockTest 被编译时会发生什么。

Heisenbenchmark 原则

当编写一个微基准测试来测量像 synchronization 的(编程)语言原语性能时,你就是在和 Heisenberg 原则作斗争。你想要测量操作X有多快,所以你不想做除了X之外的任何其它操作。但是通常得到一个什么也不做的基准测试。在这样的基准测试中,编译器会在你没有意识到的情况下执行部分或全部优化,导致测试运行得比期望快。如果你将额外的代码Y加入到你的基准测试中,你测量的就是X+Y的性能,也就是对X的测量中中引入了“噪声”。更糟糕的是Y的存在改变了JIT优化X的行为。编写一个良好的微基准测试意味着在“(额外)操作与数据流依赖不足以阻止编译器优化你的整个测试程序”和“(额外)操作太过多导致你想测量的东西被‘噪声’淹没”之间找到一个难以捉摸的平衡。

因为运行时编译使用 profiling 数据来指导它的优化操作,JIT可能会更好地优化测试代码,而不同于真实代码。与所有基准测试一样,编译器有能力优化整个测试代码是一项重要的风险。因为它将意识到基准测试代码没有做任何事情,或者所产出的结果没有被用于任何操作。编写有效的基准测试需要让编译器变“愚”,使它不会去除这些“无效”代码(即使真的是无效的代码)。两个 Incrementer 类中计数变量的使用方式没能使编译器变“愚”。编译器在消除无效代码方面通常比我们认为的更聪明。

事实上这个问题与 synchronization 是语言内建特性复合在一起。JIT编译器被允许在处理同步代码块时有一些自由,来降低性能开销。在有些情况下,同步操作可以被完全移除,而且相邻同步代码块对同一个同步元(monitor)的同步操作可以被合并。如果我们在测量同步的开销,这些优化真的会打击到我们。因为我们不知道有多少(这个案例中几乎是所有)同步操作被优化掉了。更糟的是,JIT优化 SyncTest.increment() 中什么也没做的代码的方式与真实世界中的程序是非常不同的。

更糟的来了。这个微基准测试的表面目的是测试 synchronization(synchronized 原语) 和 ReentrantLock 哪个更快。因为 synchronization 是语言内建的,而 ReentrantLock 是一个普通的 Java 类,所以编译器对“什么也没做的 synchronization”和“获取 ReentrantLock”的优化是不同的。这个优化使得什么也没做的 synchronization 看起来更快。因为编译器对这两个测试案例的优化是不同的,在真实世界中场景中的优化方式也是不同的,所以这份测试程序的执行结果几乎不能能告诉我们两者(synchronization 和 ReentrantLock)在真实世界场景中的性能差异。

 

无效代码消除

在《Dynamic compilation and performance measurement》中,我论述了基准测试中无效代码消除的问题。这是因为基准通常不会对计算结果做任何处理,导致编译器通常可以基准测试中的整块代码,进而扭曲耗时测量。这份测试代码多个地方有该问题。事实上,编译器的无效代码消除对我们(的程序)不一定是致命的。但在该案例中,这个问题在两个代码执行路径上能导致不同程度的优化,系统性地扭曲我们的测量。

两个 Incrementer 类旨在做一些什么也不做的工作(对一个变量做递增)。但是一个聪明的JVM会察觉到这两个计数变量从来都不会被读取,因此可以消除相关代码(包括递增操作代码)。这就是我们存在严重问题的地方——现在 SyncIncrementer.increment() 中的 synchronized 代码块是空的,编译器可以将它完整移除,而 LockIncrementer.increment() 中仍然有锁相关的代码,编译器可能无法完全消除。你可能会认为这(就)是 synchronization 的一种优势,即,编译器能更容易地消除它。但是这种现象更多得是出现在什么也没做的基准测试代码中,而不是真实世界中编写良好的代码。(也就是说真实应用场景中,我们几乎不会写这些什么没做的代码)

问题就是,编译器优化会更好地优化其中一种实现方式,但是这个差异只存在于什么也没做的基准测试中。这导致这种比较 synchronization 和 ReentrantLock 性能的方法会如此之难。

 

循环展开与锁合并

即使编译器没有消除对计数变量的操作,它依然会以不同的方式优化两个 increment() 方法。一种标准的优化是 循环展开。编译器会展开循环代码,从而减少代码分支数量。被展开的迭代数量取决于循环体内代码的数量。很明显,LockIncrementer.increment() 方法中循环体内的代码比 SyncIncrementer.increment() 方法中的多。进一步说,当 SyncIncrementer.incrementer() 被展开且方法调用被内联,这个被展开的循环就是一个“加锁——增值——解锁”操作组合的序列。因为所有这些加锁操作都是针对同一个同步元(monitor),编译器可以执行锁合并(lock coalescing 或 lock coarsening)来合并相邻的同步代码块。这意味着 SyncIncrementer 执行的同步操作比期望的更少。(更糟的是,加锁操作被合并后,同步体内将只包含一个“增值”操作序列,其消耗仅相当于单次加法操作。而且,如果该操作被重复应用,整个循环将被折叠为单个同步块,内部操作就是单个“counter=10000000”操作。真实世界中的JVM真的能执行这些优化。)

再次说明,问题不仅仅是(编译器)优化器会优化我们的基准测试,而是它对其中一种实现方式所做的优化程度与另一种实现方式不同,且对任何一种实现方式所采用的优化方法与真实世界中的不一样。

 

缺陷计分卡(清单)

这些是此基准测试未达到其创建者目的的原因(这是不详尽的清单):

  • 没有执行预热,而且没有考虑JIT执行所花的时间
  • 此测试容易受到单态调用转换和随后的反优化影响
  • synchronized 块和 ReentrantLock 所包含的代码块是无效代码,这会扭曲JIT优化它们的方式;它可以消除整个 synchronization 测试
  • 该测试程序想测量锁的性能,但是它没有包含(多线程)竞争的影响,而且它只在一个单处理系统上运行
  • 该测试程序没有在足够多的不同平台上运行
  • 编译器能在 synchronization 上执行的优化比 ReentrantLock 上更多,但是这些优化方式并不会对真实世界中使用 synchronization 的程序有多大帮助

问错误的问题,得到错误的答案

微基准测试的可怕之处是它们总是产出一个数字,即使这个数字是毫无意义的。它们确实测量一些东西,只是我们不知道到底是什么。通常,它们只测量特定微基准测试的性能,不做其它事。但是很容易说服你自己你的基准测试测量的是某个特定的(数据)结构,并错误地得出对那个(数据)结构的性能结论。

即使当你编写了一个优秀的基准测试,你的测量结果也可能只在你执行的系统上有效。如果你在一个单处理器且内存较小的笔记本系统上运行,你可能无法得出任何关于它在服务器系统上的性能结论。底层并发原语(如 compare-and-swap)的性能在不同硬件架构系统上的性能表现会有相当大的不同。

事实时,试图通过单个数字来测量类似“synchronization 性能”这样的目标是不可能的。synchronized 的性能在不同JVM、处理器、工作负荷、JIT活动、处理器数量、被同步代码数量等条件下都会不同。你能做得最好的就是在一系列不同的平台上运行一系列基准测试,并寻找结果中的相似性。只有那时你才可以开始对 synchronization 的性能下结论。

在 JSR 166(java.util.concurrent)测试过程的基准测试中,不同平台上的性能曲线的形状非常不同。硬件结构操作(construct)(如,CAS)的性能在不同平台和不同处理器数量的场景中表现不同(如,在单处理器系统中,CAS永远不会失败)。内存屏障的性能在单个 Intel P4 超线程(一个芯片两个处理器核心)场景下比两个 P4 要快。而且这个两种场景下的性能都不同于与 Sparc 处理器。所以你能做得最好的就是尝试构建“典型”的(基准测试)样例并在“典型”的硬件环境中测试,并期望这会产出一些与我们的真实程序在真实硬件上性能表现相关的“领悟”。“典型”(基准测试)的样例有哪个些构成要素?一个融合了计算、IO、同步、竞争、内存局部性、分配行为、上下文切换、系统调用、线程间通信的基准测试才会近似与真实世界的应用。也就是说,现实的基准测试看上去非常像一个真实世界的程序。

 

如何编写一个完美的微基准测试

所以,你如何才能写出一个完美的微基准测试呢?首先,编写一个优化良好的JIT。与那些已经编写其它优化良好的JIT的人见面(他们很容易找,因为没有优化良好的JIT不多)。邀请他们吃晚餐,并交换关于如何使Java字节码运行更快的性能伎俩故事。阅读几百篇关于优化 Java 代码执行的论文,并且写几篇。那时候你将拥有编写测量某种东西开销的良好准测试的能力,像同步、对象池、虚方法调用。

 

你在开玩笑吗?

你可能会认为上述编写良好微基准测试的秘诀过于保守。但是编写良好的微基准测试确实需要动态编译、优化和 JVM 实现技术方面的大量知识。为了编写一个真的会测试你所想测试对象的测试程序,你不得不理解“编译器将会做何操作”、“动态编译所得代码的性能特征”、“测试代码与典型的真实世界代码在使用相同(数据)结构上有何不同”。没有这个了解程度,你将无法得知你的测试程序是否测量了你所想要的东西。

 

所以你该做什么?

如果你真的想知道 synchronization 是否比替代的锁机制快(或者其它类似的微性能问题),你该做什么?一个观点是“相信专家”(这多大多数开发者来说都不太合适)。在 ReentrantLock 类的开发中 JSR 166 EG 的成员在很多不同平台上运行了即使没有上千也有几百小时的性能测试,检查了JIT编译出来的机器码,仔细钻研了测试结果。然后他们微调了代码重来。在开发和研究这些类时运用了大量关于JIT和微处理器行为的经验与详细理解。但还是很不幸地无法基于单个基准测试程序结果得出总结,尽管我们非常希望能够得出结论。另一个观点是把你的注意力集中于“宏观”的基准测试。即,编写一些真实世界的程序,用两种方式都实现一遍,开发一套现实的负载生成策略,并分别测量你的程序在现实的负载条件和现实的部署配置下两种不同实现方式的性能。这是大量的工作,但是它将会使你更接近于你要寻找的答案。

 

相关话题

 

分享到:
评论

相关推荐

    计算机类英语词汇-170

    At a Test Harness (一个测试组件) - **定义**: 用于验证软件模块功能的测试框架。 - **应用**: 在软件开发过程中,确保代码质量。 #### 38. Pixel-Wise (像素级别) - **定义**: 按照每个像素单独处理图像的方式。...

    自动化工具_鼠标连点_ZMClick_兼容性软件_1743961858.zip

    自动化工具_鼠标连点_ZMClick_兼容性软件_1743961858

    增材制造仿真:基于ANSYS Workbench的温度场与应力场分析及关键技术解析

    内容概要:本文详细介绍了增材制造仿真技术,特别是利用ANSYS Workbench进行温度场和应力场的模拟。文中涵盖了多种增材制造工艺,如WAAM(电弧增材制造)、SLM(选择性激光熔化)和同轴送粉增材制造。重点讨论了双椭球热源模型的应用及其在APDL命令流中的实现,强调了热源参数的选择和调整。此外,还探讨了单道单层和多道多层增材仿真的特点和难点,包括网格划分、材料定义、边界条件设置等方面的具体操作步骤。通过实例展示了如何优化仿真流程,提高仿真精度,从而为增材制造的实际应用提供理论支持和技术指导。 适合人群:从事增材制造研究的技术人员、工程师及相关领域的科研人员。 使用场景及目标:适用于希望深入理解和掌握增材制造仿真技术的研究者和从业者,旨在帮助他们优化制造工艺,提升产品质量,解决实际生产中的难题。 其他说明:文章不仅提供了详细的理论讲解,还分享了许多实用的操作技巧和经验总结,使读者能够更快地上手并应用于实际工作中。

    DeepSeek 提示词设计、幻觉避免与应用(50页).pptx

    DeepSeek 提示词设计、幻觉避免与应用(50页)

    中医馆小程序观摩demo源码.zip

    《中医馆小程序观摩 demo 源码简介》 本资源为中医馆小程序观摩 demo 源码,是极具价值的学习资源。该源码完整呈现了中医馆小程序的架构与功能实现逻辑,涵盖多个核心板块。 从界面设计看,有简洁美观且贴合中医文化风格的用户交互页面,方便患者操作。在功能模块方面,具备挂号预约系统,患者能便捷预约专家号、普通号,还可查看医生排班;诊疗记录管理功能,详细记录患者病情、诊断、药方等,便于医生回顾与后续治疗参考;中药知识科普板块,介绍各类中药材功效、适用病症及使用禁忌,增加患者对中医的认知。 其代码结构清晰,注释丰富,无论是对于初学者了解小程序开发流程、熟悉前端框架运用,还是有一定经验的开发者深入研究特定功能实现,如数据库交互优化、接口安全管理等,都有极大帮助。开发者可借鉴其设计模式与技术选型,快速上手并开发出功能更完善、用户体验更佳的中医馆相关小程序或其他医疗健康类应用。总之,这是一份不可多得的学习资源,助力开发者提升技术水平,拓展开发思路。

    第19.2章-【星曈科技】openmv H7 plus openmv视觉循迹功能-完成视觉识别指定区域 OpenMV视觉模块循迹_巡线功能 STM32F103C8T6视觉巡线小车.md

    第19.2章-【星曈科技】openmv H7 plus openmv视觉循迹功能-完成视觉识别指定区域 OpenMV视觉模块循迹_巡线功能 STM32F103C8T6视觉巡线小车

    量化交易捉妖主升浪指标:基于均线与RSI的股票突破捕捉系统设计

    内容概要:本文介绍了“捉妖主升浪指标”的具体实现代码及其应用逻辑。该指标主要用于股票市场技术分析,通过一系列复杂的数学运算和条件判断,识别股票价格走势中的关键节点。核心公式包括平台突破、主力筹码、RSI等技术指标的计算,以及多个条件组合来判断不同类型的“捉妖”信号,如突破捉妖、拉升捉妖、逼空捉妖、超跌捉妖等。每个信号都有特定的颜色标识,并在图表上显示相应的提示信息。 适合人群:对股票交易和技术分析有一定了解的投资者,尤其是希望借助技术指标提高选股效率的专业人士或资深股民。 使用场景及目标:①用于股票市场的技术分析,帮助投资者识别潜在的强势股;②结合其他分析工具,为买卖决策提供参考依据;③通过捕捉短期爆发性上涨机会,优化投资组合收益。 阅读建议:由于该指标涉及较多专业术语和技术细节,建议读者先熟悉常用的技术分析方法,再深入研究此指标的具体实现逻辑。同时,在实际操作中应结合市场情况灵活运用,避免机械套用。

    基于STM32、ESP8266、微信小程序搭建的MQTT智能家居设计资料.zip

    《基于STM32、ESP8266、微信小程序搭建的MQTT智能家居设计资料.zip》是一份极具价值的学习资源。它详细展示了如何结合嵌入式技术与移动互联网技术,实现智能家居系统的远程控制与实时监控。通过这份资料,学习者可以深入了解STM32微控制器的硬件设计、ESP8266 Wi-Fi模块的通信编程,以及微信小程序的用户界面开发。该资源不仅提供了完整的项目代码和文档说明,还强调了数据安全和用户体验的重要性,非常适合物联网和智能家居领域的初学者及进阶者学习参考。

    小程序源码-海报组件-生成朋友圈分享海报并生成图片.zip

    小程序源码 - 海报组件:打造个性化朋友圈分享体验 本资源是一份极具实用价值的学习资源,聚焦于小程序中的海报组件开发。它旨在帮助开发者掌握生成朋友圈分享海报并生成图片的关键技术,为丰富小程序的社交分享功能提供有力支持。 该源码详细展示了如何构建一个高效且美观的海报组件。从布局设计到样式设置,再到动态数据的绑定与处理,每一个环节都经过精心打磨。通过巧妙运用前端技术,实现了根据不同场景和需求,灵活生成个性化的朋友圈分享海报。无论是电商推广、活动宣传还是内容分享,都能借助此组件快速创建吸引人的海报图片,提升用户分享的积极性和传播效果。 这份资源对于想要深入学习小程序开发的人员而言,是一份不可多得的学习素材。它不仅提供了实际可运行的代码示例,还蕴含着丰富的开发思路和技巧,能够帮助开发者快速理解并应用相关技术,提升在小程序海报组件开发方面的能力,为打造更具竞争力的小程序应用奠定坚实基础。

    西门子1500PLC在大型物流中心分拣线的应用:硬件组态、SCL编程与HMI配置

    内容概要:本文详细介绍了基于西门子1500 PLC的大型物流分拣线项目,涵盖了硬件组态、SCL编程和人机界面(HMI)配置。项目涉及18个远程IO模块和39个ST40 CPU,旨在提高分拣效率和准确性。硬件部分重点描述了远程IO模块和扫码枪的配置,确保系统的稳定性和通信质量。软件部分则通过SCL编程实现了分拣逻辑,包括根据扫码信息确定分拣路径、处理不同运营模式下的分拣策略等。HMI配置方面,使用了KTP1200触摸屏提供实时监控和操作界面,增强了系统的易用性和透明度。 适合人群:从事工业自动化、PLC编程和物流系统设计的专业工程师和技术人员。 使用场景及目标:适用于需要构建或优化自动化分拣系统的物流中心。主要目标是提高分拣效率和准确性,减少人工干预,确保系统的稳定运行。 其他说明:文中还分享了一些实用技巧和调试经验,如处理扫码枪数据、优化HMI界面设计等,为后续项目实施提供了宝贵的参考资料。

    地铁图小程序源码.zip

    1、该资源内项目代码经过严格调试,下载即用确保可以运行! 2、该资源适合计算机相关专业(如计科、人工智能、大数据、数学、电子信息等)正在做课程设计、期末大作业和毕设项目的学生、或者相关技术学习者作为学习资料参考使用。 3、该资源包括全部源码,需要具备一定基础才能看懂并调试代码。 地铁图小程序源码.zip地铁图小程序源码.zip地铁图小程序源码.zip 地铁图小程序源码.zip地铁图小程序源码.zip地铁图小程序源码.zip 地铁图小程序源码.zip地铁图小程序源码.zip地铁图小程序源码.zip 地铁图小程序源码.zip地铁图小程序源码.zip地铁图小程序源码.zip 地铁图小程序源码.zip地铁图小程序源码.zip地铁图小程序源码.zip 地铁图小程序源码.zip地铁图小程序源码.zip地铁图小程序源码.zip 地铁图小程序源码.zip地铁图小程序源码.zip地铁图小程序源码.zip 地铁图小程序源码.zip地铁图小程序源码.zip地铁图小程序源码.zip 地铁图小程序源码.zip地铁图小程序源码.zip地铁图小程序源码.zip

    经营分析中的指标勾稽关系(15页PPT).pptx

    经营分析中的指标勾稽关系(15页PPT)

    重复文件分析.exe

    本工具用于扫描您指定的硬盘分区或目录下的文件,自动根据文件大小,创建时间以及文件名称排列并显示可能重复的文件。

    MySQL数据库备份多循环.sh

    MySQL数据库备份多循环.sh

    基于Lumerical FDTD仿真的逆向设计片上功率分束器项目详解

    内容概要:本文详细介绍了基于Lumerical FDTD仿真的逆向设计片上功率分束器项目。首先概述了Lumerical FDTD仿真的基础概念及其在光学仿真领域的应用,接着重点讲解了逆向设计的核心思想,即通过定义目标输出反推得到满足条件的结构。文中提供了多个代码片段展示如何创建仿真区域、定义目标分束比例、更新仿真结构、运行仿真、计算误差并调整结构参数。此外,还分享了完整的项目工程文件和一份详细的1.7万字报告,涵盖了从项目背景、原理阐述到具体仿真步骤、逆向设计算法分析、结果讨论和优化方向等多个方面。 适合人群:从事光通信、集成光学研究的专业人士,尤其是对Lumerical FDTD仿真和逆向设计感兴趣的科研人员和技术开发者。 使用场景及目标:适用于希望深入了解片上功率分束器设计原理及其实现方法的研究人员;帮助读者掌握Lumerical FDTD仿真工具的具体使用技巧;为相关领域的创新设计提供参考案例和支持。 其他说明:该项目不仅展示了逆向设计理念的实际应用,还强调了在实际操作中遇到的问题及解决方案,如参数调整、结构优化、网格划分等。同时,通过大量实例代码和图表,使读者能够更好地理解和复现实验过程。

    【Linux系统性能优化】基于缓存机制的磁盘I/O加速与内存管理:提升Web服务器和数据库查询效率的技术解析

    内容概要:本文详细介绍了Linux缓存机制及其对系统性能的提升作用。文章首先通过类比解释了缓存机制的重要性,随后阐述了缓存机制的工作原理,包括读取过程、写入策略(回写和写透缓存)、以及缓存回收策略(如LRU)。接着,文章分类讲解了不同类型的缓存,如文件系统缓存、页面缓存和缓冲缓存,并具体说明了它们在Web服务器和数据库查询中的应用。最后,文章介绍了如何查看和管理Linux缓存,包括释放缓存和管理Swap交换分区的方法,展示了缓存机制在实际场景中的性能优化效果。 适合人群:对Linux系统有一定了解的系统管理员、开发者和技术爱好者。 使用场景及目标:①理解Linux缓存机制的基本概念和工作原理;②掌握如何通过缓存机制优化系统性能,特别是在Web服务器和数据库查询方面;③学会使用相关命令查看和管理缓存,以应对不同的系统需求和性能问题。 其他说明:随着技术的发展,未来的缓存技术将在智能化和自动化方向取得更大突破,为系统管理员提供更便捷的管理和监控手段。文章鼓励读者深入探索Linux缓存机制,以更好地理解和优化系统性能。

    欧姆龙PLC Modbus RTU主站通讯简易实现方法与应用

    内容概要:本文详细介绍了如何利用欧姆龙PLC通过RS485通信实现Modbus RTU主站通讯的方法。主要内容分为硬件配置和软件编程两大部分。硬件方面,强调了RS485模块的选择和设置,特别是波特率、数据位、停止位等参数的配置。软件编程则涵盖了读操作和写操作的具体实现步骤,包括CRC校验的应用、功能码的选择以及轮询机制的设计。此外,文中提供了具体的代码示例,帮助读者更好地理解和实施。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是熟悉欧姆龙PLC和Modbus RTU协议的用户。 使用场景及目标:①快速搭建欧姆龙PLC作为Modbus RTU主站的通信环境;②简化配置流程,提高工作效率;③解决传统配置方法中存在的复杂性和不确定性。 其他说明:文中提到的功能块适用于多种型号的欧姆龙PLC,如CP1H、CP1L、CJ2M等。同时,针对特定型号(如CP2E)给出了特殊的注意事项。文中还提供了一些实用的小技巧,如使用外部CRC校验工具简化读操作,以及采用轮询方式优化多从站通信。

    计算机课后习题精选(附详细解答).pdf

    内容概要:本文档《计算机课后习题精选(附详细解答)》涵盖数据结构与算法、操作系统、计算机网络、数据库、编程基础五大模块,通过精选习题及其详细解答,帮助读者巩固计算机专业基础知识。例如,在数据结构与算法部分,提供了链表节点删除、二叉树最大深度计算等典型题目及其Python代码实现;操作系统部分讲解了进程调度(FCFS、SJF)、页面置换算法(FIFO、LRU);计算机网络涉及IP子网划分、TCP三次握手过程;数据库部分包括SQL查询语句编写、事务ACID特性解释;编程基础则包含时间复杂度分析和字符串处理。每个章节都配有详细的解题步骤与代码实现。 适合人群:计算机相关专业学生以及希望提升计算机基础知识的技术人员。 使用场景及目标:①作为计算机专业课程的辅助教材,帮助学生课后复习和加深理解;②为准备技术面试的求职者提供练习素材,提高解决实际问题的能力;③通过做题实践加强对计算机基础理论知识的掌握。 阅读建议:读者应结合自身情况选择性地进行练习,对于不懂的地方可以反复研读解答过程并尝试自己动手实现代码,同时建议建立错题本记录易错知识点,以便日后复习。

    计算机科学中数据结构学习教程

    数据结构是计算机科学中的一个核心概念,它研究数据的组织、存储和操作方式。掌握数据结构对于解决复杂问题、优化程序性能以及进行高效的数据处理至关重要。本文将系统地介绍数据结构的基本概念、常见类型以及应用实例,帮助读者从入门到精通数据结构。数据结构是计算机科学中的基础概念,掌握它对于解决复杂问题和优化程序性能至关重要。通过学习线性表、树、图、哈希表等常见数据结构,以及排序和查找等算法,可以显著提高编程能力。同时,利用丰富的学习资源,如在线教程、书籍和开源项目,可以更系统地掌握数据结构知识。

    (源码)基于Webpack的模块化打包项目.zip

    # 基于Webpack的模块化打包项目 ## 项目简介 本项目使用Webpack作为模块化打包工具,实现了项目代码的模块化打包,支持开发环境和生产环境的构建,支持CSS和JavaScript文件的编译和打包,支持第三方库的分离,以及CSS文件的压缩和分离。 ## 项目的主要特性和功能 1. 模块化打包使用Webpack作为模块化打包工具,支持开发环境和生产环境的构建。 2. CSS和JavaScript编译支持CSS和JavaScript文件的编译和打包,支持LessScss预处理器。 3. 第三方库分离支持第三方库的分离,减少打包后的文件体积。 4. CSS压缩和分离支持CSS文件的压缩和分离,优化打包后的文件体积。 5. 代码热替换支持代码热替换(HMR),提升开发效率。 6. 文件哈希值支持文件名哈希值,提升浏览器缓存效率。 7. 打包分析支持使用webpackbundleanalyzer插件对打包后的代码进行可视化分析。 ## 安装使用步骤

Global site tag (gtag.js) - Google Analytics