如何优化基于mXparser的Java函数绘图算法运行速度
mXparser 函数绘图程序性能优化方案
你当前代码的核心性能损耗完全来自循环内的重复无效操作,并非函数计算本身的开销,按以下方案调整后可轻松将耗时压缩到目标区间,甚至更低:
核心优化点
- 将单次初始化逻辑全部移到循环外
你的x取值从-13到17、步长0.01,总共要执行3000次循环迭代。当前代码在每次循环中都重复读取函数字符串、新建Function对象、新建Expression对象、重复设置画笔颜色——mXparser解析函数表达式、初始化语法树是最重的操作,这部分重复操作占了当前2.5秒耗时的80%以上。这些操作和x的当前取值完全无关,整个绘制流程只需要执行一次。 - 复用mXparser参数对象,避免重复创建
代表x变量的Argument对象不需要每次循环新建,只需要在循环外初始化一次,每次迭代直接调用setArgumentValue()方法更新x的取值即可,能省掉大量对象创建、垃圾回收的开销。 - 简化绘制逻辑,减少冗余对象创建
当前每个点都新建一个Line2D.Double对象,3000个短生命周期对象会带来不必要的创建和GC开销,且当前线段终点的y值计算逻辑有误。直接调用Graphics2D的drawLine()方法传入像素坐标即可,不需要额外创建图形对象;画笔颜色只需要在绘制函数前设置一次,不需要每个点重复设置。 - 可选:按屏幕像素精度调整步长
你的代码里x方向1单位对应50像素,也就是每像素对应0.02的x轴单位,当前0.01的步长会导致每两个像素才绘制一个点,属于过采样。把步长调整为0.02可以直接减少一半计算量,肉眼完全看不出绘制效果的差异。 - 可选:关闭mXparser默认日志
在程序初始化阶段加一句org.mariuszgromada.math.mxparser.mXparser.setConsoleLogsVisible(false);,关闭库的默认控制台输出,能省掉少量日志打印开销。
优化后参考代码
package rechner; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import org.mariuszgromada.math.mxparser.Argument; import org.mariuszgromada.math.mxparser.Expression; import org.mariuszgromada.math.mxparser.Function; import org.mariuszgromada.math.mxparser.mXparser; import oberflaeche.GUI_Controller; public class drawFunction { // 若要匹配像素精度可改为0.02,速度提升一倍 private static final double STEP_VALUE = 0.01; private static final int PIXEL_PER_UNIT = 50; private int width; private int height; Graphics2D g2; GUI_Controller GUI; Color cornBlue = new Color(100, 149, 237); Font mediumFont = new Font("Times New Roman", Font.ITALIC, 25); Font bigFont = new Font("Times New Roman", Font.ITALIC, 50); // 类初始化时关闭mXparser日志 static { mXparser.setConsoleLogsVisible(false); } public drawFunction(int w, int h, GUI_Controller newGUI) { width = w; height = h; GUI = newGUI; } public void drawFunc(Graphics2D g2) { int originX = width / 2 - 100; int originY = height / 2 + 100; // 所有只需要初始化一次的对象全部挪到循环外 String function = GUI.getFunction(); Function func = new Function(function); Argument xArg = new Argument("x = 0"); Expression exp = new Expression("f(x)", func, xArg); // 提前设置画笔颜色 g2.setColor(cornBlue); // 保存上一个点的像素坐标,用来连线段 int lastXPixel = 0; int lastYPixel = 0; boolean isFirstPoint = true; for(double x = -13; x <= 17; x += STEP_VALUE) { // 只更新x参数值,不新建对象 xArg.setArgumentValue(x); double y = exp.calculate(); // 过滤非数值结果(比如函数无定义的点) if (Double.isNaN(y) || Double.isInfinite(y)) { isFirstPoint = true; continue; } int xPixel = (int)(originX + x * PIXEL_PER_UNIT); int yPixel = (int)(originY - y * PIXEL_PER_UNIT); if (isFirstPoint) { isFirstPoint = false; } else { // 相邻点连线段,不需要新建Line2D对象 g2.drawLine(lastXPixel, lastYPixel, xPixel, yPixel); } lastXPixel = xPixel; lastYPixel = yPixel; } } }
预期效果
保持0.01步长不变的情况下,上述优化后的代码绘制x^2这类简单函数的耗时基本在0.5-1秒区间,如果将步长调整为0.02,耗时可进一步降到0.3-0.6秒,完全满足你要求的1-1.6秒的性能目标。
内容的提问来源于stack exchange,提问作者developerDave
相关产品推荐
相关产品推荐

