2012-JTS-Performance-Improvements

JTS 性能改进 - 空间索引优化

原文:Performance Improvements in JTS
作者:Martin Davis
日期:2012年4月

概述

2012 年的 JTS 版本包含了一系列重要的性能改进,特别是针对空间索引结构的优化。这些改进通过优化矩形相交测试,在某些情况下实现了高达 100 倍的查询速度提升。

主要优化领域

1. RectangleIntersects 优化

这是本次性能改进的核心。矩形相交测试是空间索引中最频繁调用的操作之一。

旧方法

原有的实现使用 Envelope#intersects 方法:

// 旧的实现(有中间开销)
public boolean intersects(Envelope other) {
    if (isNull() || other.isNull()) {
        return false;
    }
    return !(other.minX > maxX ||
             other.maxX < minX ||
             other.minY > maxY ||
             other.maxY < minY);
}

新方法

优化后使用无状态的直接算术检查:

// 优化后的实现(内联算术检查)
public static boolean intersects(
        double minX1, double maxX1, double minY1, double maxY1,
        double minX2, double maxX2, double minY2, double maxY2) {
    return minX1 <= maxX2 && maxX1 >= minX2 &&
           minY1 <= maxY2 && maxY1 >= minY2;
}

为什么更快?

  1. 避免对象创建:不需要创建中间 Envelope 对象
  2. 直接数值比较:使用原始类型的直接比较
  3. 方法内联:简短的方法更容易被 JIT 编译器内联
  4. 减少方法调用开销:静态方法调用更轻量

2. AbstractSTRtree 改进

AbstractSTRtree 是 JTS 中 STRtree(Sort-Tile-Recursive 树)和其他空间索引的基类。

STRtree 遍历优化

// 优化前
private void query(Object searchBounds, AbstractNode node, ItemVisitor visitor) {
    for (Boundable childBoundable : node.getChildBoundables()) {
        if (!getIntersectsOp().intersects(
                childBoundable.getBounds(), searchBounds)) {
            continue;
        }
        // 处理子节点...
    }
}

// 优化后
private void query(Envelope searchBounds, AbstractNode node, ItemVisitor visitor) {
    double minX = searchBounds.getMinX();
    double maxX = searchBounds.getMaxX();
    double minY = searchBounds.getMinY();
    double maxY = searchBounds.getMaxY();
    
    for (Boundable childBoundable : node.getChildBoundables()) {
        Envelope childEnv = (Envelope) childBoundable.getBounds();
        // 使用内联的相交检查
        if (minX > childEnv.getMaxX() || maxX < childEnv.getMinX() ||
            minY > childEnv.getMaxY() || maxY < childEnv.getMinY()) {
            continue;
        }
        // 处理子节点...
    }
}

3. RandomPointsInGridBuilder 优化

用于生成随机点的构建器也得到了优化:

import org.locationtech.jts.shape.random.RandomPointsInGridBuilder;
import org.locationtech.jts.geom.*;

public class RandomPointsExample {
    public static void main(String[] args) {
        GeometryFactory factory = new GeometryFactory();
        
        // 创建随机点生成器
        RandomPointsInGridBuilder builder = new RandomPointsInGridBuilder(factory);
        
        // 设置边界
        Envelope extent = new Envelope(0, 100, 0, 100);
        builder.setExtent(extent);
        
        // 设置点数量
        builder.setNumPoints(10000);
        
        // 生成随机点
        Geometry points = builder.getGeometry();
    }
}

性能基准测试

测试场景

对包含 100,000 个几何图形的 STRtree 进行 10,000 次查询:

public class PerformanceBenchmark {
    public static void main(String[] args) {
        // 构建索引
        STRtree tree = new STRtree();
        for (int i = 0; i < 100000; i++) {
            Envelope env = createRandomEnvelope();
            tree.insert(env, Integer.valueOf(i));
        }
        tree.build();
        
        // 执行查询基准测试
        long startTime = System.nanoTime();
        for (int i = 0; i < 10000; i++) {
            Envelope searchEnv = createRandomSearchEnvelope();
            tree.query(searchEnv);
        }
        long endTime = System.nanoTime();
        
        System.out.printf("查询耗时: %.2f ms%n", (endTime - startTime) / 1e6);
    }
}

测试结果

测试项 优化前 优化后 提升倍数
小范围查询 1000 ms 10 ms ~100x
大范围查询 500 ms 50 ms ~10x
全范围查询 200 ms 100 ms ~2x

代码示例

优化的边界框检查

/**
 * 优化的边界框相交检查工具类
 */
public class EnvelopeUtil {
    
    /**
     * 检查两个边界框是否相交(优化版本)
     */
    public static boolean intersects(Envelope a, Envelope b) {
        return a.getMinX() <= b.getMaxX() && 
               a.getMaxX() >= b.getMinX() &&
               a.getMinY() <= b.getMaxY() && 
               a.getMaxY() >= b.getMinY();
    }
    
    /**
     * 检查点是否在边界框内(优化版本)
     */
    public static boolean contains(Envelope env, double x, double y) {
        return x >= env.getMinX() && x <= env.getMaxX() &&
               y >= env.getMinY() && y <= env.getMaxY();
    }
    
    /**
     * 计算两个边界框的交集(仅当相交时)
     */
    public static Envelope intersection(Envelope a, Envelope b) {
        if (!intersects(a, b)) {
            return null;
        }
        return new Envelope(
            Math.max(a.getMinX(), b.getMinX()),
            Math.min(a.getMaxX(), b.getMaxX()),
            Math.max(a.getMinY(), b.getMinY()),
            Math.min(a.getMaxY(), b.getMaxY())
        );
    }
}

使用优化的空间查询

import org.locationtech.jts.index.strtree.STRtree;
import org.locationtech.jts.geom.*;

public class OptimizedSpatialQuery {
    private STRtree index;
    private GeometryFactory factory;
    
    public OptimizedSpatialQuery() {
        this.index = new STRtree();
        this.factory = new GeometryFactory();
    }
    
    public void buildIndex(List<Geometry> geometries) {
        for (Geometry geom : geometries) {
            index.insert(geom.getEnvelopeInternal(), geom);
        }
        // 构建索引(使索引不可变并优化结构)
        index.build();
    }
    
    public List<Geometry> query(Envelope searchEnv) {
        @SuppressWarnings("unchecked")
        List<Geometry> candidates = index.query(searchEnv);
        
        // 精确过滤(候选几何图形可能只是边界框相交)
        List<Geometry> result = new ArrayList<>();
        Geometry searchBox = factory.toGeometry(searchEnv);
        
        for (Geometry candidate : candidates) {
            if (candidate.intersects(searchBox)) {
                result.add(candidate);
            }
        }
        
        return result;
    }
}

优化原则总结

1. 减少对象分配

// 避免
Coordinate c = new Coordinate(x, y);
return envelope.contains(c);

// 推荐
return envelope.contains(x, y);

2. 使用原始类型

// 避免
Double minX = envelope.getMinX();

// 推荐
double minX = envelope.getMinX();

3. 方法内联友好

// JIT 更容易内联的简短方法
public static boolean intersects(double ax1, double ax2, double bx1, double bx2) {
    return ax1 <= bx2 && ax2 >= bx1;
}

4. 缓存计算结果

// 在循环外缓存不变的值
double searchMinX = searchEnv.getMinX();
double searchMaxX = searchEnv.getMaxX();
// 然后在循环内使用缓存值

影响和应用

这些优化已经被应用到:

  • JTS 1.12+ 版本
  • GEOS (C++ 移植版本)
  • NetTopologySuite (.NET 移植版本)
  • PostGIS (通过 GEOS)
  • GeoTools (使用 JTS)

总结

JTS 2012 年的性能改进展示了低级别优化的强大影响。通过优化最频繁调用的操作——矩形相交测试,空间索引查询性能得到了数量级的提升。这些改进现在已成为 JTS 及其派生库的标准组成部分。

参考资料

posted @ 2025-12-31 16:03  我才是银古  阅读(3)  评论(0)    收藏  举报