You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何C语言实现的欧几里得距离函数运行速度慢于Java?排查与优化实践

问题背景

我分别用C和Java实现了3D点的欧几里得距离函数并做基准测试,最初发现C版本运行时间约1.688852秒,而Java版本仅需0.355038秒。即使尝试移除sqrt、手动内联代码、修改函数签名为直接接收6个double坐标(避免指针访问),C版本的耗时也没明显改善。

编译和运行环境:

  • C:用cc -O2 main.c -lm编译
  • Java:IntelliJ IDEA默认JVM选项(Java 8,OpenJDK)

初始测试代码

C版本

#include <math.h>
#include <stdio.h>
#include <time.h>
typedef struct point3d {
 double x;
 double y;
 double z;
} point3d_t;
double distance(point3d_t *from, point3d_t *to);
int main(int argc, char const *argv[]) {
 point3d_t from = {.x = 2.3, .y = 3.45, .z = 4.56};
 point3d_t to = {.x = 5.678, .y = 3.45, .z = -9.0781};
 double time = 0.0;
 int count = 10000000;
 for (size_t i = 0; i < count; i++) {
 clock_t tic = clock();
 double d = distance(&from, &to);
 clock_t toc = clock();
 time += ((double) (toc - tic) / CLOCKS_PER_SEC);
 }
 printf("Elapsed: %f seconds\n", time);
 return 0;
}
double distance(point3d_t *from, point3d_t *to) {
 double dx = to->x - from->x;
 double dy = to->y - from->y;
 double dz = to->z - from->z;
 double d2 = (dx * dx) + (dy * dy) + (dz + dz); // 此处存在bug:dz+dz应为dz*dz
 return sqrt(d2);
}

Java版本

public class App {
 static Random rnd = new Random();
 public static void main( String[] args ) {
 var sw = new StopWatch();
 var time = 0.0;
 var count = 10000000;
 for (int i = 0; i < count; i++) {
 var from = Vector3D.of(rnd.nextDouble(), rnd.nextDouble(), rnd.nextDouble());
 var to = Vector3D.of(rnd.nextDouble(), rnd.nextDouble(), rnd.nextDouble());
 sw.start();
 var dist = distance(from, to);
 sw.stop();
 time += sw.getTime(TimeUnit.NANOSECONDS);
 sw.reset();
 }
 System.out.printf("Time: %f seconds\n", time / 1e09);
 }
 public static double distance(Vector3D from, Vector3D to) {
 var dx = to.getX() - from.getX();
 var dy = to.getY() - from.getY();
 var dz = to.getZ() - from.getZ();
 return Math.sqrt((dx * dx) + (dy * dy) + (dz * dz));
 }
}

注:Java中使用随机值是为了避免JVM执行结果缓存、跳过计算之类的优化操作。


第一次优化:修正基准测试方法

最初的基准测试存在明显缺陷:C版本在循环内对单次函数调用单独计时,引入了大量额外开销;且Java和C的测试场景不一致(C用固定点,Java用随机点)。于是更新代码统一测试逻辑:

更新后的C代码

#include <math.h>
#include <stdio.h>
#include <time.h>
typedef struct point3d {
 double x;
 double y;
 double z;
} point3d_t;
double distance(point3d_t *from, point3d_t *to);
int main(int argc, char const *argv[]) {
 point3d_t from = {.x = 2.3, .y = 3.45, .z = 4.56};
 point3d_t to = {.x = 5.678, .y = 3.45, .z = -9.0781};
 struct timespec fs;
 struct timespec ts;
 long time = 0;
 int count = 10000000;
 double dist = 0;
 clock_gettime(CLOCK_REALTIME, &fs);
 for (size_t i = 0; i < count; i++) {
 dist = distance(&from, &to);
 }
 clock_gettime(CLOCK_REALTIME, &ts);
 time = ts.tv_nsec - fs.tv_nsec;
 if (dist == 0.001) {
 printf("hello\n");
 }
 printf("Elapsed: %f sec\n", (double) time / 1e9);
 return 0;
}
double distance(point3d_t *from, point3d_t *to) {
 double dx = to->x - from->x;
 double dy = to->y - from->y;
 double dz = to->z - from->z;
 double d2 = (dx * dx) + (dy * dy) + (dz + dz); // 依然保留dz+dz的bug
 return sqrt(d2);
}

更新后的Java代码

public class App {
 static Random rnd = new Random();
 public static void main( String[] args ) {
 var from = Vector3D.of(rnd.nextDouble(), rnd.nextDouble(), rnd.nextDouble());
 var to = Vector3D.of(rnd.nextDouble(), rnd.nextDouble(), rnd.nextDouble());
 var time = 0.0;
 var count = 10000000;
 double dist = 0.0;
 var start = System.nanoTime();
 for (int i = 0; i < count; i++) {
 dist = distance(from, to);
 }
 var end = System.nanoTime();
 time = end - start;
 if (dist == rnd.nextDouble()) {
 System.out.printf("hello! %f\n", dist);
 }
 dist = dist + 1;
 System.out.printf("Time: %f sec\n", (double) time / 1e9);
 System.out.printf("Yohoo! %f\n", dist);
 }
 public static double distance(Vector3D from, Vector3D to) {
 var dx= to.getX() - from.getX();
 var dy = to.getY() - from.getY();
 var dz = to.getZ() - from.getZ();
 return Math.sqrt((dx * dx) + (dy * dy) + (dz * dz));
 }
}

此时用gcc -Wall -std=gnu99 -O2 main.c -lm编译C代码,测试结果:

  • C耗时:0.06323秒
  • Java耗时:0.006325秒
    C版本依然慢于Java,问题根源尚未找到。

最终解决:修复代码bug + 优化编译选项

经过排查,发现两个核心问题:

  1. 代码逻辑bug:C版本的distance函数中,计算平方和时错误地写成了dz + dz,正确应为dz * dz。这个错误会导致d2可能为负数,调用sqrt时触发错误处理逻辑,大幅拖慢执行速度。
  2. 编译选项限制:GCC默认会为数学函数保留errno错误检查,即使结果不可能出错,也会产生额外开销。添加-fno-math-errno选项可以关闭该检查,让sqrt调用更高效。

最终可用代码

C版本

#include <math.h>
#include <stdio.h>
#include <time.h>
typedef struct point3d {
 double x;
 double y;
 double z;
} point3d_t;
double distance(point3d_t *from, point3d_t *to);
int main(int argc, char const *argv[]) {
 point3d_t from = {.x = 2.3, .y = 3.45, .z = 4.56};
 point3d_t to = {.x = 5.678, .y = 3.45, .z = -9.0781};
 struct timespec fs;
 struct timespec ts;
 long time = 0;
 int count = 10000000;
 double dist = 0;
 clock_gettime(CLOCK_REALTIME, &fs);
 for (size_t i = 0; i < count; i++) {
 dist = distance(&from, &to);
 }
 clock_gettime(CLOCK_REALTIME, &ts);
 time = ts.tv_nsec - fs.tv_nsec;
 printf("hello %f \n", dist);
 printf("Elapsed: %f ns\n", (double) time);
 printf("Elapsed: %f sec\n", (double) time / 1e9);
 return 0;
}
double distance(point3d_t *from, point3d_t *to) {
 double dx = (to->x) - (from->x);
 double dy = (to->y) - (from->y);
 double dz = (to->z) - (from->z);
 double d2 = (dx * dx) + (dy * dy) + (dz * dz); // 修复dz*dz的bug
 return sqrt(d2);
}

编译命令:gcc -O2 -std=gnu99 -fno-math-errno main.c -lm

Java版本

public class App {
 static Random rnd = new Random();
 public static void main( String[] args ) {
 var from = Vector3D.of(2.3, 3.45, 4.56);
 var to = Vector3D.of(5.678, 3.45, -9.0781);
 var time = 0.0;
 var count = 10000000;
 double dist = 0.0;
 var start = System.nanoTime();
 for (int i = 0; i < count; i++) {
 dist = distance(from, to);
 }
 var end = System.nanoTime();
 time = end - start;
 System.out.printf("Yohoo! %f\n", dist);
 System.out.printf("Time: %f ns\n", (double) time / 1e9);
 }
 public static double distance(Vector3D from, Vector3D to) {
 var dx = to.getX() - from.getX();
 var dy = to.getY() - from.getY();
 var dz = to.getZ() - from.getZ();
 var d2 = (dx * dx) + (dy * dy) + (dz * dz);
 return Math.sqrt(d2);
 }
}

最终测试结果

  • C版本耗时:几乎为0秒(271纳秒)
  • Java版本耗时:0.007217秒

现在C版本的速度完全超过了Java,性能表现恢复正常!


内容的提问来源于stack exchange,提问作者kovac

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 16:32:52