Reduce memory consumption of incremental compilation on dev server

This commit is contained in:
Alexey Andreev 2019-03-05 16:50:16 +03:00
parent 35730d665f
commit 573c5f6064
10 changed files with 717 additions and 482 deletions

View File

@ -525,7 +525,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|| methodInjectors.containsKey(method.getReference())) { || methodInjectors.containsKey(method.getReference())) {
continue; continue;
} }
if (!method.hasModifier(ElementModifier.NATIVE) && method.getProgram() == null) { if (!method.hasModifier(ElementModifier.NATIVE) && !method.hasProgram()) {
continue; continue;
} }

View File

@ -15,6 +15,9 @@
*/ */
package org.teavm.cache; package org.teavm.cache;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -26,6 +29,9 @@ import org.teavm.model.ProgramCache;
public class InMemoryProgramCache implements ProgramCache { public class InMemoryProgramCache implements ProgramCache {
private Map<MethodReference, Item> cache = new HashMap<>(); private Map<MethodReference, Item> cache = new HashMap<>();
private Map<MethodReference, Item> newItems = new HashMap<>(); private Map<MethodReference, Item> newItems = new HashMap<>();
private InMemorySymbolTable symbolTable = new InMemorySymbolTable();
private InMemorySymbolTable fileSymbolTable = new InMemorySymbolTable();
private ProgramIO io = new ProgramIO(new InMemorySymbolTable(), new InMemorySymbolTable());
@Override @Override
public Program get(MethodReference method, CacheStatus cacheStatus) { public Program get(MethodReference method, CacheStatus cacheStatus) {
@ -37,13 +43,23 @@ public class InMemoryProgramCache implements ProgramCache {
if (Arrays.stream(item.dependencies).anyMatch(cacheStatus::isStaleClass)) { if (Arrays.stream(item.dependencies).anyMatch(cacheStatus::isStaleClass)) {
return null; return null;
} }
try {
return item.program; ByteArrayInputStream input = new ByteArrayInputStream(item.program);
return io.read(input);
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
@Override @Override
public void store(MethodReference method, Program program, Supplier<String[]> dependencies) { public void store(MethodReference method, Program program, Supplier<String[]> dependencies) {
newItems.put(method, new Item(program, dependencies.get().clone())); try {
ByteArrayOutputStream output = new ByteArrayOutputStream();
io.write(program, output);
newItems.put(method, new Item(output.toByteArray(), dependencies.get().clone()));
} catch (IOException e) {
throw new RuntimeException(e);
}
} }
public void commit() { public void commit() {
@ -62,13 +78,15 @@ public class InMemoryProgramCache implements ProgramCache {
public void invalidate() { public void invalidate() {
cache.clear(); cache.clear();
newItems.clear(); newItems.clear();
symbolTable.invalidate();
fileSymbolTable.invalidate();
} }
static final class Item { static final class Item {
final Program program; final byte[] program;
final String[] dependencies; final String[] dependencies;
Item(Program program, String[] dependencies) { Item(byte[] program, String[] dependencies) {
this.program = program; this.program = program;
this.dependencies = dependencies; this.dependencies = dependencies;
} }

View File

@ -0,0 +1,47 @@
/*
* Copyright 2019 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.cache;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class InMemorySymbolTable implements SymbolTable {
private List<String> symbols = new ArrayList<>();
private Map<String, Integer> indexes = new HashMap<>();
@Override
public String at(int index) {
return symbols.get(index);
}
@Override
public int lookup(String symbol) {
Integer index = indexes.get(symbol);
if (index == null) {
index = symbols.size();
symbols.add(symbol);
indexes.put(symbol, index);
}
return index;
}
public void invalidate() {
symbols.clear();
indexes.clear();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,100 @@
/*
* Copyright 2019 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.cache;
import java.io.IOException;
import java.io.InputStream;
public class VarDataInput {
private static final int DATA = 0x7F;
private static final int NEXT = 0x80;
private InputStream input;
public VarDataInput(InputStream input) {
this.input = input;
}
public int readUnsigned() throws IOException {
int value = 0;
int pos = 0;
int b;
do {
b = input.read();
value |= (b & DATA) << pos;
pos += 7;
} while ((b & NEXT) != 0);
return value;
}
public int readSigned() throws IOException {
int value = readUnsigned();
return (value & 1) == 0 ? (value >> 1) : -(value >> 1);
}
public long readUnsignedLong() throws IOException {
long value = 0;
int pos = 0;
int b;
do {
b = input.read();
value |= ((long) b & DATA) << pos;
pos += 7;
} while ((b & NEXT) != 0);
return value;
}
public long readSignedLong() throws IOException {
long value = readUnsignedLong();
return (value & 1) == 0 ? (value >> 1) : -(value >> 1);
}
public float readFloat() throws IOException {
int exponent = readSigned() + 127;
int mantissa = Integer.reverse(readUnsigned()) >>> 8;
boolean sign = (mantissa & (1 << 23)) != 0;
int bits = mantissa & ((1 << 23) - 1);
bits |= exponent << 23;
if (sign) {
bits |= 1 << 31;
}
return Float.intBitsToFloat(bits);
}
public double readDouble() throws IOException {
int exponent = readSigned() + 1023;
long mantissa = Long.reverse(readUnsignedLong()) >>> 11;
boolean sign = (mantissa & (1L << 52)) != 0;
long bits = mantissa & ((1L << 52) - 1);
bits |= (long) exponent << 52;
if (sign) {
bits |= 1L << 53;
}
return Double.longBitsToDouble(bits);
}
public String read() throws IOException {
int sz = readUnsigned();
char[] chars = new char[sz];
for (int i = 0; i < sz; ++i) {
chars[i] = (char) readUnsigned();
}
return new String(chars);
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 2019 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.cache;
import java.io.IOException;
import java.io.OutputStream;
public class VarDataOutput {
private static final int DATA = 0x7F;
private static final int NEXT = 0x80;
private OutputStream output;
public VarDataOutput(OutputStream output) {
this.output = output;
}
public void writeUnsigned(int value) throws IOException {
while ((value & DATA) != value) {
output.write((value & DATA) | NEXT);
value >>>= 7;
}
output.write(value);
}
public void writeSigned(int value) throws IOException {
writeUnsigned(value < 0 ? ((-value) << 1) | 1 : value << 1);
}
public void writeUnsigned(long value) throws IOException {
while ((value & DATA) != value) {
output.write((int) (value & DATA) | NEXT);
value >>>= 7;
}
output.write((int) value);
}
public void writeSigned(long value) throws IOException {
writeUnsigned(value < 0 ? ((-value) << 1) | 1 : value << 1);
}
public void writeFloat(float value) throws IOException {
int bits = Float.floatToRawIntBits(value);
boolean sign = (bits & (1 << 31)) != 0;
int exponent = (bits >> 23) & ((1 << 8) - 1);
int mantissa = bits & ((1 << 23) - 1);
if (sign) {
mantissa |= 1 << 23;
}
writeSigned(exponent - 127);
writeUnsigned(Integer.reverse(mantissa << 8));
}
public void writeDouble(double value) throws IOException {
long bits = Double.doubleToRawLongBits(value);
boolean sign = (bits & (1L << 63)) != 0;
int exponent = (int) (bits >> 52) & ((1 << 11) - 1);
long mantissa = bits & ((1L << 52) - 1);
if (sign) {
mantissa |= 1L << 52;
}
writeSigned(exponent - 1023);
writeUnsigned(Long.reverse(mantissa << 11));
}
public void write(String s) throws IOException {
writeUnsigned(s.length());
for (int i = 0; i < s.length(); ++i) {
writeUnsigned(s.charAt(i));
}
}
}

View File

@ -115,29 +115,21 @@ public class MethodHolder extends MemberHolder implements MethodReader {
public Program getProgram() { public Program getProgram() {
if (program == null && programSupplier != null) { if (program == null && programSupplier != null) {
program = programSupplier.apply(this); program = programSupplier.apply(this);
if (program != null) {
program.setMethod(this);
}
programSupplier = null; programSupplier = null;
} }
return program; return program;
} }
public void setProgram(Program program) { public void setProgram(Program program) {
if (this.program != null) {
this.program.setMethod(null);
}
this.program = program; this.program = program;
this.programSupplier = null; this.programSupplier = null;
if (this.program != null) { }
this.program.setMethod(this);
} public boolean hasProgram() {
return program != null || programSupplier != null;
} }
public void setProgramSupplier(Function<MethodHolder, Program> programSupplier) { public void setProgramSupplier(Function<MethodHolder, Program> programSupplier) {
if (this.program != null) {
this.program.setMethod(null);
}
this.program = null; this.program = null;
this.programSupplier = programSupplier; this.programSupplier = programSupplier;
} }

View File

@ -21,7 +21,6 @@ import java.util.List;
public class Program implements ProgramReader { public class Program implements ProgramReader {
private List<BasicBlock> basicBlocks = new ArrayList<>(2); private List<BasicBlock> basicBlocks = new ArrayList<>(2);
private List<Variable> variables = new ArrayList<>(); private List<Variable> variables = new ArrayList<>();
private MethodHolder method;
private boolean packed; private boolean packed;
private int lastUsedRegister; private int lastUsedRegister;
@ -150,17 +149,4 @@ public class Program implements ProgramReader {
} }
return variables.get(index); return variables.get(index);
} }
@Override
public MethodReference getMethodReference() {
return method != null ? method.getReference() : null;
}
MethodHolder getMethod() {
return method;
}
void setMethod(MethodHolder method) {
this.method = method;
}
} }

View File

@ -25,6 +25,4 @@ public interface ProgramReader {
int variableCount(); int variableCount();
VariableReader variableAt(int index); VariableReader variableAt(int index);
MethodReference getMethodReference();
} }

View File

@ -21,10 +21,6 @@ import static org.junit.Assert.assertThat;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test; import org.junit.Test;
import org.teavm.model.BasicBlock; import org.teavm.model.BasicBlock;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;
@ -171,25 +167,4 @@ public class ProgramIOTest {
throw new AssertionError("This exception should not be thrown", e); throw new AssertionError("This exception should not be thrown", e);
} }
} }
private static class InMemorySymbolTable implements SymbolTable {
private List<String> symbols = new ArrayList<>();
private Map<String, Integer> indexes = new HashMap<>();
@Override
public String at(int index) {
return symbols.get(index);
}
@Override
public int lookup(String symbol) {
Integer index = indexes.get(symbol);
if (index == null) {
index = symbols.size();
symbols.add(symbol);
indexes.put(symbol, index);
}
return index;
}
}
} }