* Update MemoryIT to report median render and request time * Add TreeGrid and TreeTable performance test UIs Addresses the collapsed cases for #8849, #8850tags/8.1.0.alpha2
package com.vaadin.tests.performance; | |||||
import java.util.List; | |||||
import java.util.Optional; | |||||
import javax.servlet.annotation.WebServlet; | |||||
import com.vaadin.annotations.VaadinServletConfiguration; | |||||
import com.vaadin.data.HierarchyData; | |||||
import com.vaadin.data.provider.InMemoryHierarchicalDataProvider; | |||||
import com.vaadin.server.VaadinServlet; | |||||
import com.vaadin.tests.data.bean.Address; | |||||
import com.vaadin.tests.data.bean.Person; | |||||
import com.vaadin.ui.TreeGrid; | |||||
public class TreeGridMemory extends AbstractBeansMemoryTest<TreeGrid<Person>> { | |||||
public static final String PATH = "/tree-grid-memory/"; | |||||
@WebServlet(urlPatterns = PATH | |||||
+ "*", name = "TreeGridServlet", asyncSupported = true) | |||||
@VaadinServletConfiguration(ui = TreeGridMemory.class, productionMode = false) | |||||
public static class Servlet extends VaadinServlet { | |||||
} | |||||
@Override | |||||
protected TreeGrid<Person> createComponent() { | |||||
TreeGrid<Person> treeGrid = new TreeGrid<>(); | |||||
treeGrid.addColumn(Person::getFirstName).setCaption("First Name"); | |||||
treeGrid.addColumn(Person::getLastName).setCaption("Last Name"); | |||||
treeGrid.addColumn(person -> Optional.ofNullable(person.getAddress()) | |||||
.map(Address::getStreetAddress).orElse(null)) | |||||
.setCaption("Street"); | |||||
treeGrid.addColumn(person -> Optional.ofNullable(person.getAddress()) | |||||
.map(Address::getPostalCode).map(Object::toString).orElse("")) | |||||
.setCaption("Zip"); | |||||
treeGrid.addColumn(person -> Optional.ofNullable(person.getAddress()) | |||||
.map(Address::getCity).orElse(null)).setCaption("City"); | |||||
return treeGrid; | |||||
} | |||||
@Override | |||||
protected void setInMemoryContainer(TreeGrid<Person> treeGrid, | |||||
List<Person> data) { | |||||
HierarchyData<Person> hierarchyData = new HierarchyData<>(); | |||||
if (data.size() % 2 == 0) { | |||||
// treat list as if it were a balanced binary tree | |||||
hierarchyData.addItem(null, data.get(0)); | |||||
int n = 0; | |||||
while (2 * n + 2 < data.size()) { | |||||
hierarchyData.addItems(data.get(n), | |||||
data.subList(2 * n + 1, 2 * n + 3)); | |||||
n++; | |||||
} | |||||
} else { | |||||
hierarchyData.addItems(null, data); | |||||
} | |||||
treeGrid.setDataProvider( | |||||
new InMemoryHierarchicalDataProvider<>(hierarchyData)); | |||||
} | |||||
@Override | |||||
protected void setBackendContainer(TreeGrid<Person> component, | |||||
List<Person> data) { | |||||
throw new UnsupportedOperationException(); | |||||
} | |||||
} |
package com.vaadin.tests.performance; | |||||
import java.util.List; | |||||
import java.util.Optional; | |||||
import javax.servlet.annotation.WebServlet; | |||||
import com.vaadin.annotations.VaadinServletConfiguration; | |||||
import com.vaadin.server.VaadinServlet; | |||||
import com.vaadin.tests.data.bean.Address; | |||||
import com.vaadin.tests.data.bean.Person; | |||||
import com.vaadin.v7.data.Item; | |||||
import com.vaadin.v7.data.util.HierarchicalContainer; | |||||
import com.vaadin.v7.ui.TreeTable; | |||||
public class TreeTableMemory extends AbstractBeansMemoryTest<TreeTable> { | |||||
public static final String PATH = "/tree-table-memory/"; | |||||
@WebServlet(urlPatterns = PATH | |||||
+ "*", name = "TreeTableServlet", asyncSupported = true) | |||||
@VaadinServletConfiguration(ui = TreeTableMemory.class, productionMode = false, widgetset = "com.vaadin.v7.Vaadin7WidgetSet") | |||||
public static class Servlet extends VaadinServlet { | |||||
} | |||||
@Override | |||||
protected TreeTable createComponent() { | |||||
TreeTable treeTable = new TreeTable(); | |||||
return treeTable; | |||||
} | |||||
@Override | |||||
protected void setInMemoryContainer(TreeTable treeTable, | |||||
List<Person> data) { | |||||
HierarchicalContainer container = new HierarchicalContainer(); | |||||
container.addContainerProperty("firstName", String.class, null); | |||||
container.addContainerProperty("lastName", String.class, null); | |||||
container.addContainerProperty("street", String.class, null); | |||||
container.addContainerProperty("zip", String.class, null); | |||||
container.addContainerProperty("city", String.class, null); | |||||
if (data.size() % 2 == 0) { | |||||
createItem(0, container, data); | |||||
int n = 0; | |||||
while (2 * n + 2 < data.size()) { | |||||
for (int i : new Integer[] { 1, 2 }) { | |||||
createItem(2 * n + i, container, data); | |||||
container.setParent(2 * n + i, n); | |||||
} | |||||
n++; | |||||
} | |||||
} else { | |||||
for (int i = 0; i < data.size(); i++) { | |||||
createItem(i, container, data); | |||||
} | |||||
} | |||||
treeTable.setContainerDataSource(container); | |||||
} | |||||
private void createItem(int index, HierarchicalContainer container, | |||||
List<Person> data) { | |||||
Item item = container.addItem(index); | |||||
item.getItemProperty("firstName") | |||||
.setValue(data.get(index).getFirstName()); | |||||
item.getItemProperty("lastName") | |||||
.setValue(data.get(index).getLastName()); | |||||
item.getItemProperty("street") | |||||
.setValue(Optional.ofNullable(data.get(index).getAddress()) | |||||
.map(Address::getStreetAddress).orElse(null)); | |||||
item.getItemProperty("zip") | |||||
.setValue(Optional.ofNullable(data.get(index).getAddress()) | |||||
.map(Address::getPostalCode).map(Object::toString) | |||||
.orElse("")); | |||||
item.getItemProperty("city") | |||||
.setValue(Optional.ofNullable(data.get(index).getAddress()) | |||||
.map(Address::getCity).orElse(null)); | |||||
} | |||||
@Override | |||||
protected void setBackendContainer(TreeTable component, List<Person> data) { | |||||
throw new UnsupportedOperationException(); | |||||
} | |||||
} |
*/ | */ | ||||
package com.vaadin.tests.performance; | package com.vaadin.tests.performance; | ||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import org.apache.commons.lang3.StringUtils; | import org.apache.commons.lang3.StringUtils; | ||||
import org.junit.Assert; | import org.junit.Assert; | ||||
import org.junit.Test; | import org.junit.Test; | ||||
@Category(MeasurementTest.class) | @Category(MeasurementTest.class) | ||||
public class MemoryIT extends SingleBrowserTest { | public class MemoryIT extends SingleBrowserTest { | ||||
private static final int MAX_ITERATIONS = 20; | |||||
@Test | @Test | ||||
public void measureMemory() { | public void measureMemory() { | ||||
printTeamcityStats("grid-v8-one-item-size", | |||||
getGridSize(GridMemory.PATH, 1)); | |||||
printTeamcityStats("grid-v7-one-item-size", | |||||
getGridSize(CompatibilityGridMemory.PATH, 1)); | |||||
printTeamcityStats("grid-v8-100thousand-items-size", | |||||
getGridSize(GridMemory.PATH, 100000)); | |||||
printTeamcityStats("grid-v7-100thousand-items-size", | |||||
getGridSize(CompatibilityGridMemory.PATH, 100000)); | |||||
performTest(GridMemory.PATH, 1, "grid-v8-one-item-"); | |||||
performTest(CompatibilityGridMemory.PATH, 1, "grid-v7-one-item-"); | |||||
performTest(GridMemory.PATH, 100000, "grid-v8-100thousand-items-"); | |||||
performTest(CompatibilityGridMemory.PATH, 100000, | |||||
"grid-v7-100thousand-items-"); | |||||
performTest(TreeGridMemory.PATH, 1, "tree-grid-one-item-"); | |||||
performTest(TreeTableMemory.PATH, 1, "tree-table-one-item-"); | |||||
performTest(TreeGridMemory.PATH, 100000, | |||||
"tree-grid-100thousand-items-"); | |||||
performTest(TreeTableMemory.PATH, 100000, | |||||
"tree-table-100thousand-items-"); | |||||
} | } | ||||
@Override | @Override | ||||
protected void closeApplication() { | protected void closeApplication() { | ||||
} | } | ||||
private long getGridSize(String path, int itemsCount) { | |||||
// Repeat until we get consecutive results within 0.1% of each other | |||||
private void performTest(String path, int itemsCount, | |||||
String teamcityStatPrefix) { | |||||
double lastResult = 0; | double lastResult = 0; | ||||
int stableNumber = 0; | int stableNumber = 0; | ||||
for (int i = 0; i < 500; i++) { | |||||
List<Long> renderingTimes = new ArrayList<>(); | |||||
List<Long> requestTimes = new ArrayList<>(); | |||||
for (int i = 0; i < MAX_ITERATIONS; i++) { | |||||
openUI(path, itemsCount); | openUI(path, itemsCount); | ||||
renderingTimes.add(testBench().totalTimeSpentRendering()); | |||||
requestTimes.add(testBench().totalTimeSpentServicingRequests()); | |||||
long currentResult = Long | long currentResult = Long | ||||
.parseLong(findElement(By.id("memory")).getText()); | .parseLong(findElement(By.id("memory")).getText()); | ||||
close(); | close(); | ||||
} | } | ||||
lastResult = currentResult; | lastResult = currentResult; | ||||
if (stableNumber == 5) { | if (stableNumber == 5) { | ||||
return currentResult; | |||||
System.out.println( | |||||
"Memory usage stabilized after " + i + " iterations"); | |||||
printTeamcityStats(teamcityStatPrefix + "size", currentResult); | |||||
printTeamcityStats(teamcityStatPrefix + "rendering-time", | |||||
median(renderingTimes)); | |||||
printTeamcityStats(teamcityStatPrefix + "request-time", | |||||
median(requestTimes)); | |||||
return; | |||||
} | |||||
if (i == MAX_ITERATIONS) { | |||||
Assert.fail("Memory size does not stabilize"); | |||||
} | } | ||||
} | } | ||||
} | |||||
Assert.fail("Memory size does not stabilize"); | |||||
return -1; | |||||
private long median(List<Long> values) { | |||||
values.sort(Long::compareTo); | |||||
int middle = values.size() / 2; | |||||
if (values.size() % 2 == 1) { | |||||
return values.get(middle); | |||||
} else { | |||||
return (values.get(middle - 1) + values.get(middle)) / 2; | |||||
} | |||||
} | } | ||||
private boolean approx(double num1, double num2, double epsilon) { | private boolean approx(double num1, double num2, double epsilon) { | ||||
private void openUI(String path, int itemsNumber) { | private void openUI(String path, int itemsNumber) { | ||||
getDriver().get(StringUtils.strip(getBaseURL(), "/") + path + "?items=" | getDriver().get(StringUtils.strip(getBaseURL(), "/") + path + "?items=" | ||||
+ itemsNumber); | + itemsNumber); | ||||
Assert.assertTrue(isElementPresent(By.className("v-grid"))); | |||||
Assert.assertTrue(isElementPresent(By.className("v-grid")) | |||||
|| isElementPresent(By.className("v-table"))); | |||||
} | } | ||||
private void close() { | private void close() { |