Ich experimentierte mit einem Java-Port von some C# code und ich war überrascht zu sehen, dass javac 1.8.0_60 einen getfield
Opcode ausgab, jedes Mal, wenn ein Objektfeld zugegriffen wurde.Wäre es für einen Java-Compiler zulässig, getfield-Opcodes nach dem ersten Zugriff wegzulassen?
ist die Java-Code:
public class BigInteger
{
private int[] bits;
private int sign;
//...
public byte[] ToByteArray()
{
if (sign == 0)
{
return new byte[] { 0 };
}
byte highByte;
int nonZeroDwordIndex = 0;
int highDword;
if (bits == null)
{
highByte = (byte)((sign < 0) ? 0xff : 0x00);
highDword = sign;
}
else if (sign == -1)
{
highByte = (byte)0xff;
assert bits.length > 0;
assert bits[bits.length - 1] != 0;
while (bits[nonZeroDwordIndex] == 0)
{
nonZeroDwordIndex++;
}
highDword = ~bits[bits.length - 1];
if (bits.length - 1 == nonZeroDwordIndex)
{
highDword += 1;
}
}
else
{
assert sign == 1;
highByte = 0x00;
highDword = bits[bits.length - 1];
}
byte msb;
int msbIndex;
if ((msb = (byte)(highDword >>> 24)) != highByte)
{
msbIndex = 3;
}
else if ((msb = (byte)(highDword >>> 16)) != highByte)
{
msbIndex = 2;
}
else if ((msb = (byte)(highDword >>> 8)) != highByte)
{
msbIndex = 1;
}
else
{
msb = (byte)highDword;
msbIndex = 0;
}
boolean needExtraByte = (msb & 0x80) != (highByte & 0x80);
byte[] bytes;
int curByte = 0;
if (bits == null)
{
bytes = new byte[msbIndex + 1 + (needExtraByte ? 1 : 0)];
assert bytes.length <= 4;
}
else
{
bytes = new byte[4 * (bits.length - 1) + msbIndex + 1 + (needExtraByte ? 1 : 0)];
for (int i = 0; i < bits.length - 1; i++)
{
int dword = bits[i];
if (sign == -1)
{
dword = ~dword;
if (i <= nonZeroDwordIndex)
{
dword = dword + 1;
}
}
for (int j = 0; j < 4; j++)
{
bytes[curByte++] = (byte)dword;
dword >>>= 8;
}
}
}
for (int j = 0; j <= msbIndex; j++)
{
bytes[curByte++] = (byte)highDword;
highDword >>>= 8;
}
if (needExtraByte)
{
bytes[bytes.length - 1] = highByte;
}
return bytes;
}
}
Wie javap berichtet, javac 1.8.0_60 erzeugt folgende Bytecode:
public byte[] ToByteArray(); Code: 0: aload_0 1: getfield #3 // Field sign:I 4: ifne 15 7: iconst_1 8: newarray byte 10: dup 11: iconst_0 12: iconst_0 13: bastore 14: areturn 15: iconst_0 16: istore_2 17: aload_0 18: getfield #2 // Field bits:[I 21: ifnonnull 48 24: aload_0 25: getfield #3 // Field sign:I 28: ifge 37 31: sipush 255 34: goto 38 37: iconst_0 38: i2b 39: istore_1 40: aload_0 41: getfield #3 // Field sign:I 44: istore_3 45: goto 193 48: aload_0 49: getfield #3 // Field sign:I 52: iconst_m1 53: if_icmpne 156 56: iconst_m1 57: istore_1 58: getstatic #11 // Field $assertionsDisabled:Z 61: ifne 80 64: aload_0 65: getfield #2 // Field bits:[I 68: arraylength 69: ifgt 80 72: new #12 // class java/lang/AssertionError 75: dup 76: invokespecial #13 // Method java/lang/AssertionError."":()V 79: athrow 80: getstatic #11 // Field $assertionsDisabled:Z 83: ifne 109 86: aload_0 87: getfield #2 // Field bits:[I 90: aload_0 91: getfield #2 // Field bits:[I 94: arraylength 95: iconst_1 96: isub 97: iaload 98: ifne 109 101: new #12 // class java/lang/AssertionError 104: dup 105: invokespecial #13 // Method java/lang/AssertionError."":()V 108: athrow 109: aload_0 110: getfield #2 // Field bits:[I 113: iload_2 114: iaload 115: ifne 124 118: iinc 2, 1 121: goto 109 124: aload_0 125: getfield #2 // Field bits:[I 128: aload_0 129: getfield #2 // Field bits:[I 132: arraylength 133: iconst_1 134: isub 135: iaload 136: iconst_m1 137: ixor 138: istore_3 139: aload_0 140: getfield #2 // Field bits:[I 143: arraylength 144: iconst_1 145: isub 146: iload_2 147: if_icmpne 193 150: iinc 3, 1 153: goto 193 156: getstatic #11 // Field $assertionsDisabled:Z 159: ifne 178 162: aload_0 163: getfield #3 // Field sign:I 166: iconst_1 167: if_icmpeq 178 170: new #12 // class java/lang/AssertionError 173: dup 174: invokespecial #13 // Method java/lang/AssertionError."":()V 177: athrow 178: iconst_0 179: istore_1 180: aload_0 181: getfield #2 // Field bits:[I 184: aload_0 185: getfield #2 // Field bits:[I 188: arraylength 189: iconst_1 190: isub 191: iaload 192: istore_3 193: iload_3 194: bipush 24 196: iushr 197: i2b 198: dup 199: istore 4 201: iload_1 202: if_icmpeq 211 205: iconst_3 206: istore 5 208: goto 254 211: iload_3 212: bipush 16 214: iushr 215: i2b 216: dup 217: istore 4 219: iload_1 220: if_icmpeq 229 223: iconst_2 224: istore 5 226: goto 254 229: iload_3 230: bipush 8 232: iushr 233: i2b 234: dup 235: istore 4 237: iload_1 238: if_icmpeq 247 241: iconst_1 242: istore 5 244: goto 254 247: iload_3 248: i2b 249: istore 4 251: iconst_0 252: istore 5 254: iload 4 256: sipush 128 259: iand 260: iload_1 261: sipush 128 264: iand 265: if_icmpeq 272 268: iconst_1 269: goto 273 272: iconst_0 273: istore 6 275: iconst_0 276: istore 8 278: aload_0 279: getfield #2 // Field bits:[I 282: ifnonnull 325 285: iload 5 287: iconst_1 288: iadd 289: iload 6 291: ifeq 298 294: iconst_1 295: goto 299 298: iconst_0 299: iadd 300: newarray byte 302: astore 7 304: getstatic #11 // Field $assertionsDisabled:Z 307: ifne 443 310: aload 7 312: arraylength 313: iconst_4 314: if_icmple 443 317: new #12 // class java/lang/AssertionError 320: dup 321: invokespecial #13 // Method java/lang/AssertionError."":()V 324: athrow 325: iconst_4 326: aload_0 327: getfield #2 // Field bits:[I 330: arraylength 331: iconst_1 332: isub 333: imul 334: iload 5 336: iadd 337: iconst_1 338: iadd 339: iload 6 341: ifeq 348 344: iconst_1 345: goto 349 348: iconst_0 349: iadd 350: newarray byte 352: astore 7 354: iconst_0 355: istore 9 357: iload 9 359: aload_0 360: getfield #2 // Field bits:[I 363: arraylength 364: iconst_1 365: isub 366: if_icmpge 443 369: aload_0 370: getfield #2 // Field bits:[I 373: iload 9 375: iaload 376: istore 10 378: aload_0 379: getfield #3 // Field sign:I 382: iconst_m1 383: if_icmpne 404 386: iload 10 388: iconst_m1 389: ixor 390: istore 10 392: iload 9 394: iload_2 395: if_icmpgt 404 398: iload 10 400: iconst_1 401: iadd 402: istore 10 404: iconst_0 405: istore 11 407: iload 11 409: iconst_4 410: if_icmpge 437 413: aload 7 415: iload 8 417: iinc 8, 1 420: iload 10 422: i2b 423: bastore 424: iload 10 426: bipush 8 428: iushr 429: istore 10 431: iinc 11, 1 434: goto 407 437: iinc 9, 1 440: goto 357 443: iconst_0 444: istore 9 446: iload 9 448: iload 5 450: if_icmpgt 474 453: aload 7 455: iload 8 457: iinc 8, 1 460: iload_3 461: i2b 462: bastore 463: iload_3 464: bipush 8 466: iushr 467: istore_3 468: iinc 9, 1 471: goto 446 474: iload 6 476: ifeq 488 479: aload 7 481: aload 7 483: arraylength 484: iconst_1 485: isub 486: iload_1 487: bastore 488: aload 7 490: areturn
Beachten Sie, dass ein getfield
Opcode vom Compiler jedes Mal emittiert wurde dass auf die Felder und bits
zugegriffen wurde.
Lese §17.4.5, Happen-Vor Auftrag von JLS8, ich sehe nicht, warum es wäre ein getfield
Opcode jedes Mal, wenn die sign
und bits
Felder zugegriffen werden (außer dem ersten) emittieren erforderlich.
Wäre es für einen Java-Compiler zulässig, nur zwei getfield
Opcodes auszugeben und die dann sichtbaren Werte der Felder in lokalen Variablen des Rahmens zu speichern?
Vielen Dank für Ihre Antwort. Ich habe [jitwatch] (https://github.com/AdoptOpenJDK/jitwatch) an einem Programm ausprobiert, das ToByteArray() wiederholt an verschiedenen Eingängen aufruft und zwischen den Läufen schläft. Der JIT kompiliert die Methode während eines Testlaufs viermal neu. In der endgültigen Kompilierung erhält die x86-64-Assembly die Werte der Felder 'sign' und 'bits' nur einmal. Ich habe auch eine Version getestet, in der ich explizit lokale Variablen verwendet habe. Die x86-64-Assembly dieser "local" -Version erhält auch die Werte der Felder "sign" und "bits" nur einmal. Interessanterweise ist die "members" Version 34 Asm Bytes kürzer. –
@JohnM "* Der Optimierer geht nicht davon aus, dass eine Variable sich niemals ändert, nur weil sie keine Sekunde hat *" => das ist nicht, was ich sage. 'stop' wird niemals in' m' modifiziert und es ist eine lokale, nicht volatile Variable. So kann der Optimierer "m" wie folgt neu schreiben: 'boolean localStop = stop; while (! localStop); '(und es tut). Wenn "Stop" "volatil" gewesen wäre, wäre das keine rechtliche Änderung. Der Sinn des Schlafes ist es, dem JIT genug Zeit zu geben, um den Code zu kompilieren und zu kompilieren. – assylias