Somebody help to to test the following program.
I got an interesting program segment from Internet which use BCB and Java to decide what kind of CPU be used. However,I don't have Java to test right now. I believe there is someone will help me to do it. At the mean time, the program segment I found is useful if it can work.
利用Java配合BCB 4.0制作CPU特征侦测器
--------------------------------------------------------------------------------
本文作者:王森
台湾交通大学科技管理研究所
moli.mt88g@nctu.edu.tw
注:本文亦适用Borland C++ Builder 5.0
?前言
笔者最近从事一个利用Java来发展密码模块的工作,由于利用纯Java语言所制作出来的密码模块效率实在不好,最后我们把脑筋动到JNI(Java Native Interface)上. 为何会想到使用JNI呢? 大家应该都知道Java程序的执行必须透过Java Virtual Machine,透过一层中介的结果,执行的效率必然比C/C++所编译出来的原生码(native code,即专属各处理器的指令集)还要慢. 事实上,在JDK内附的java.math 这个package里头,许多部分也都应用了JNI来加快运算速度(例如Big Integer运算).
?硬件优势
一旦利用的JNI,代表我们将能够连结C/C++或是Assembly所撰写的加密模块. 为了加快密码模块的performance,必须运用一些硬件上的优势,例如说当处理区块加密运算时,如果能运用一点并行处理的观念,就能够适当地加快运算速度. 以目前的PC上的处理器来说,支持平行处理能力的技术就属大家所熟知的MMX, Streaming SIMD Extension(大家也许比较熟悉的是KNI这个名词),以及3DNOW!. 这些技术其实就是实做了SIMD(Single-Instruction, Multiple-Data)的概念,允许处理器在同一时间之内,使用单一指令,就可以同时处理好几组数据.
另外,在Pentium等级以上的CPU具有利用Pipeline来加快执行速度的能力,只要调整Assembly code的排列顺序,使其符合Intel Scheduling Rules,就可以充分利用CPU里头的U-pipe与V-pipe,加快执行速度. 其实,就笔者所使用的Visual C++ 6.0与Borland C++ Builder 4.0来说,虽然都有编译器指令可以针对处理器做最佳化,但是如果您亲自去看看编译出来的结果,能然有很多地方无法尽如人意,因此如果遇到time critical的部分,仍然常常需要我们亲自去调校以改善performance.
?准备工作及注意事项
Ok,讲到这边,似乎离主题有点远了,让我们回归正题吧!
为了让我们可以在执行时期动态地依照CPU的能力来执行最佳化的程序代码,首要的工作就是要写一些函式来侦测CPU的特性,于是笔者选择了JDK 1.2以及BCB 4.0来完成整个由
Java code è JNI è Platform native code
的完整测试程序.
如果以图片来表示就如下图
如果您抓取了笔者提供的原始码,应该可以看到下面三个分别由Java与C++撰写的程序模块:
CPUTestDll.bpr CPUTestDll.cpp
实做侦测CPU特性相关函式的模块.
此为BCB 4.0之 项目文件,使用Project/make cputest的指令后,所产生的结果cputest.dll,是我们所要的.
编译注意事项 :
由于在CPUTestDll.cpp里头我们用到汇编语言指令CPUID,所以请打开Project/Option里头的Advanced Compiler次页,里头有一个叫Instruction Set的地方,请勾选Pentium,否则编译器会因为不支持此指令而产生编译错误.如果您要把编译过的结果给别人使用,建议您将Project/Option/Package次页中的Build with Run-time Package选项 以及 Linker次页中的Use Dynamic RTL选项通通取消掉.
请打开Project/Option/Directories Conditionals次页,将JDK所在目录\include
与JDK所在目录\include\win32加到Include path里头 ; 另外也在Library Path中加入 JDK所在目录\lib,否则会造成编译错误.
CPUTest.java
这个Java程序是作为其它Java程序透过JNI以呼叫CPUTestDll.dll的接口. 笔者把这个接口宣告于my.cpu这个package底下.
编译注意事项 :
编译Java程序时,请设定环境变量PATH与CLASSPATH
例如JDK安装在C:\JDK1.2这个目录,而此档案放在c:\jdk1.2\my之下,
那幺请在提示符号下命令
PATH c:\jdk1.2\bin
set CLASSPATH=C:\jdk1.2\classes;c:\JDK1.2\my
test.java
这个Java程序将利用CPUTest对象当作接口,来呼叫实做于CPUTestDll.dll内的CPU特征侦测函数
编译注意事项 :
除了2的注意事项外,请将CPUTest.java放到 <JDK安装目录>\classes\my\cpu这个目录之中,否则编译将无法通过.
?参考文件
1. JDK 1.2 on-line document
2. Intel Architecture Optimization/Reference manual Order Number: 245127-001
3. AMD 3DNOW! Technology Manual
?用JDK 实做JNI接口
首先,为了让所有的Java Code都可以使用我们的CPU特征侦测函数,我们首先必须先制作一个接口类别:
档案列表CPUTest.java
/*********************************************************************************
CPUTest.java
JNI 接口对象
1999 April 20 by 王森
**********************************************************************************/
//加入my.cpu这个package之中
package my.cpu ;
public class CPUTest {
/*以下定义每种处理器所代表的常数*/
static public final int i386 = 0 ; //不支持CPUID的处理器(可辨识)
static public final int Pentium = 1 ; //最早期的Pentium处理器(可辨识)
static public final int Pentium_M = 2 ; //Pentium with MMX 处理器(可辨识)
static public final int Pentium_2 = 3 ; //Pentium II 处理器(可辨识)
static public final int Pentium_3 = 4 ; //Pentium III处理器(可辨识)
static public final int Pentium_P = 5 ; //Pentium Pro 处理器(可辨识)
static public final int K6 = 11 ; //同Pentium with MMX
static public final int K6_2 = 12 ; //K6-2处理器((可辨识)
static public final int K6_3 = 13 ; //同K6-2
/*以下定义所有会藉由JNI来叫用的函式*/
//测试CPU是否支持CPUID指令,如果支持则传回true,否则传回false
public native boolean CheckCPUID() ;
^^^^^^ 注意,所有的JNI函式都必须在函式宣告里加上native这个修饰字
//辨识处理器是否支持MMX,如果支持则传回true,否则传回false
public native boolean CheckMMX() ;
//辨识处理器是否支持Stream SIMD Extension(即KNI),如果支持则传回true,否则传回false
public native boolean CheckSSIMD() ;
//辨识处理器是否支持AMD 3DNow,如果支持则传回true,否则传回false
public native boolean Check3DNOW() ;
//辨识CPU的等级,并传回一个整数代表CPU的等级
public native int CheckCPUTYPE() ;
//印出CPU的相关信息
public native void PrintCPUInfo() ;
note:使用此函数之前,请先呼叫前面的所有函式,因为前面的函式,除了传回真伪之外,也会设定DLL文件之中的全域变量而PrintCPUInfo会利用这些全域变量来做判定的工作.
static {
我们把实做CPU侦测函式的模块做成DLL(动态连结函式库)?,
取名叫CPUDTestDll.dll,所以在这里要加载此DLL
System.loadLibrary("CPUTestDll") ;
}
}
接着我们在提示符号下使用指令
javac CPUTest.java
编译此档案,会产生CPUTest.class这个档案.然后我们把这两个档案都移至
<JDK安装目录>/classes/my/cpu/
这个目录底下,如果没有做此动作,恐怕下面的步骤都会遇到一些错误.
最后一个步骤,就是必须产生一个引入档(Include file),我们将会在编译CPUTestDll.dll实用到这个引入档.
在提示符号下使用指令
javah my.cpu.CPUTest
就会在您目前的工作目录下看到
my_cpu_CPUTest.h
到此为止,我们已经完成了第一个阶段.
案列表my_cpu_CPUTest.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class my_cpu_CPUTest */
#ifndef _Included_my_cpu_CPUTest
#define _Included_my_cpu_CPUTest
#ifdef __cplusplus
extern "C" {
#endif
#undef my_cpu_CPUTest_i386
#define my_cpu_CPUTest_i386 0L
#undef my_cpu_CPUTest_Pentium
#define my_cpu_CPUTest_Pentium 1L
#undef my_cpu_CPUTest_Pentium_M
#define my_cpu_CPUTest_Pentium_M 2L
#undef my_cpu_CPUTest_Pentium_2
#define my_cpu_CPUTest_Pentium_2 3L
#undef my_cpu_CPUTest_Pentium_3
#define my_cpu_CPUTest_Pentium_3 4L
#undef my_cpu_CPUTest_Pentium_P
#define my_cpu_CPUTest_Pentium_P 5L
#undef my_cpu_CPUTest_K6
#define my_cpu_CPUTest_K6 11L
#undef my_cpu_CPUTest_K6_2
#define my_cpu_CPUTest_K6_2 12L
#undef my_cpu_CPUTest_K6_3
#define my_cpu_CPUTest_K6_3 13L
/*
* Class: my_cpu_CPUTest
* Method: Check3DNOW
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_my_cpu_CPUTest_Check3DNOW
(JNIEnv *, jobject);
/*
* Class: my_cpu_CPUTest
* Method: CheckCPUID
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_my_cpu_CPUTest_CheckCPUID
(JNIEnv *, jobject);
/*
* Class: my_cpu_CPUTest
* Method: CheckCPUTYPE
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_my_cpu_CPUTest_CheckCPUTYPE
(JNIEnv *, jobject);
/*
* Class: my_cpu_CPUTest
* Method: CheckMMX
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_my_cpu_CPUTest_CheckMMX
(JNIEnv *, jobject);
/*
* Class: my_cpu_CPUTest
* Method: CheckSSIMD
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL Java_my_cpu_CPUTest_CheckSSIMD
(JNIEnv *, jobject);
/*
* Class: my_cpu_CPUTest
* Method: PrintCPUInfo
* Signature: ()V
*/