程序运行时间之我见

JSP与单独运行类的时间差异解析
作者在测试程序运行时间时,发现使用JSP访问main()方法和单独运行类的时间差异大。经分析,第一次请求JSP时,WEB容器会编译JSP成Servlet并缓存,后继请求直接载入缓存;而单独运行类无缓存支持,每次都需重新编译读入内存,所以较慢。

    最近很关注XML,关心各种流行解析技术的效率,在测试程序运行时间的时候,遇到一些困难,下面先看看我提出的问题吧。
   
-----------------------------------------------------------------------------

    在一个类中,有一个叫做test()的方法需要被JSP端调用(test()主要任务是System.out.print()),于是在test()方法开始的地方加上"long lasting =System.currentTimeMillis(),"在结束的地方加上"System.out.println("运行时间:"+(System.currentTimeMillis() - lasting)+" 毫秒")"。

    然后把test()方法改成main()方法,以便单独测试类,JSP端代码作出相应调整以便访问main()方法。奇怪的事情发生了,经过反复测试,使用JSP访问main()方法消耗的时间为 451 毫秒,而单独运行这个类消耗的时间为 2864 毫秒。

    为什么会这样?是不是WEB容器的原因呢?
   
-----------------------------------------------------------------------------

    在一个人气很高的论坛上,我得到些解答,自己也总结了一些,请各位看官来发表意见:
   
    第一次请求JSP时,WEB容器将JSP发送到编译器,编译成Servlet。然后把Servlet和其他类(比如自己写的类)缓存在WEB容器中,再放入内存常驻,最后将响应结果返回给客户端;而JSP的所有后继请求,WEB容器将自动载入缓存,除非JSP或者类经过修改,否则WEB容器将不会重新执行编译并构造缓存。而单独运行类就不一样了,由于没有WEB容器缓存支持,每次运行都需要重新编译再读入内存才行,所以要慢些了。
    另外,根据测试,在JSP端第一次被调用的时候,时间和单独运行类差不多,这也印证了上面的解释吧。

   
附上源程序
类:
package com.test;
import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*;

public class MyXMLReader extends DefaultHandler{ 

 java.util.Stack tags=new java.util.Stack();

 public MyXMLReader(){
  super();
 }

 public static void main(String args[]){
 long lasting =System.currentTimeMillis();
  try{  
      SAXParserFactory sf  = SAXParserFactory.newInstance();
      SAXParser sp = sf.newSAXParser();
      MyXMLReader reader = new MyXMLReader();
      sp.parse(new InputSource("data.xml"),reader);
  }
  catch(Exception e){
   e.printStackTrace();
  }
  System.out.println("运行时间:"+(System.currentTimeMillis() - lasting)+" 毫秒");
 }

 public void characters (char ch[], int start, int length)
    throws SAXException
   {

  //从栈中得到当前节点的信息
   String tag=(String) tags.peek();
    if(tag.equals("NO") ){
   System.out.print("车牌号码:" +  new String(ch,start,length));
    }
    if(tag.equals("ADDR")){
   System.out.println("  地址:" + new String(ch,start,length));
    }
   }

 public void startElement(String uri, String localName, String qName, Attributes attrs){ 
 tags.push(qName);
 }
}

JSP:
<%@ page contentType="text/html; charset=gb2312" %>
<%@ page import="com.test.*"%>

<%
    long lasting =System.currentTimeMillis();
%>
<html>
    <body>
<%
    String args[]={""};
    MyXMLReader.main(args);
%>
    </body>
</html> 


(请注意!引用、转贴本文应注明原作者:Rosen Jiang 以及出处:
http://blog.youkuaiyun.com/rosen

<think>好的,我现在需要帮助用户在MATLAB AppDesigner中使用EditField控件实时显示程序运行时间。首先,我得理解用户的需求。用户想要在程序运行时,EditField能够动态更新显示已经过去的时间,而不是等到程序结束后才显示总时间。这涉及到实时更新UI组件的问题。 根据之前的引用内容,特别是引用[3]中的例子,用户通过循环和drawnow函数实现了进度条的实时更新。类似的方法可能适用于时间显示。不过,时间的计算和更新方式可能有所不同,因为时间需要持续累加,而不是像进度条那样基于循环的迭代次数。 首先,我需要确定如何获取程序运行时间。在MATLAB中,可以使用tic和toc函数来量时间。但是,由于AppDesigner的回调函数通常是同步执行的,如果在回调函数中使用循环,界面可能会冻结,无法实时更新。因此,可能需要使用异步处理,比如使用定时器(timer)对象,或者在循环中插入drawnow命令来强制更新界面。 引用[3]的例子中,在for循环内部调用了drawnow来实时更新EditField的值。同样的方法可以应用到这里。用户可以在开始执行耗时操作之前记录开始时间,然后在循环或处理过程中不断计算已用时间,并更新到EditField中。 另外,需要注意MATLAB AppDesigner中的控件属性赋值规则。例如,引用[5]提到,EditField的Value属性需要字符类型,所以需要将数值转换为字符串。因此,在更新EditField时,必须将时间数值转换为字符串格式。 可能的实现步骤: 1. 在App Designer中添加一个EditField控件和一个按钮控件。 2. 在按钮的回调函数中,使用tic记录开始时间。 3. 启动一个循环或执行耗时任务,期间定期计算已用时间(toc),并将其转换为字符串更新到EditField。 4. 在每次更新后调用drawnow,确保界面及时刷新。 需要注意的是,如果任务本身是长时间运行的循环,直接在其中更新EditField并调用drawnow可能会导致界面响应缓慢,但这是必要的步骤。或者,考虑将耗时任务放在一个单独的定时器中,以避免阻塞主线程。 验证过程中,还需要确保时间显示的格式正确,比如转换为分钟、秒,或者保持毫秒精度,根据用户需求调整。例如,使用sprintf('%0.2f 秒', elapsedTime)来格式化时间显示。 可能的问题包括:界面卡顿、时间更新不及时、数据类型转换错误等。需要参考引用中的例子,特别是正确转换数值到字符串,并合理使用drawnow函数。 总结解决方案:在回调函数中使用tic开始计时,在循环或处理过程中不断调用toc获取已用时间,转换为字符串后赋值给EditField.Value,并调用drawnow实时更新。</think>### 分步实现方法 #### 步骤1:添加控件 1. 在App Designer界面中拖放一个 **EditField(数值或文本类型)** 和一个 **Button** 控件。 2. 设置EditField的`Tag`属性为`TimeDisplayField`(便于代码中引用)。 #### 步骤2:编写回调函数 在按钮的回调函数中添加以下代码: ```matlab function StartButtonPushed(app, event) % 记录开始时间 tStart = tic; % 初始化EditField显示 app.TimeDisplayField.Value = '0.00 秒'; % 模拟耗时操作(例如:循环或计算) for i = 1:100 % 执行实际任务... pause(0.1); % 模拟耗时步骤 % 计算已用时间 elapsedTime = toc(tStart); % 更新EditField(转换为字符串) app.TimeDisplayField.Value = sprintf('%.2f 秒', elapsedTime); % 强制刷新界面 drawnow; end end ``` #### 步骤3:关键细节说明 1. **时间格式转换**:使用`sprintf`将数值时间格式化为字符串(如`%.2f 秒`保留两位小数)[^5]。 2. **实时刷新**:在循环内调用`drawnow`保证界面实时更新[^3]。 3. **耗时操作模拟**:若实际任务不含循环,需改用定时器(`timer`)实现异步更新(见高级优化部分)。 #### 效果演示 程序运行时,EditField会动态显示从点击按钮开始累计的时间,精度为0.01秒。 --- ### 高级优化(异步更新) 若任务本身无法拆分为循环(如单次长耗时计算),需使用`timer`对象实现非阻塞更新: ```matlab function StartButtonPushed(app, event) % 创建定时器 t = timer('ExecutionMode', 'fixedRate', 'Period', 0.1); t.StartDelay = 0; t.TimerFcn = @(~,~) updateTime(app, tic); % 传递开始时间 % 启动定时器和任务 start(t); % 执行实际任务(此处用pause模拟) pause(5); stop(t); delete(t); end function updateTime(app, tStart) elapsedTime = toc(tStart); app.TimeDisplayField.Value = sprintf('%.2f 秒', elapsedTime); end ``` --- ### 常见问题解决 1. **界面卡顿**:确保在耗时循环中频繁调用`drawnow`。 2. **时间精度不足**:减小定时器的`Period`值(如0.01秒),但会增加计算负载。 3. **数据类型错误**:使用`string()`或`sprintf`确保赋值内容为字符串[^5]。 ---
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值