Матрицы могут быть очень большими, иногда больше, чем вы можете держать в одном массиве. Вы можете увеличить максимальный размер, имея несколько массивов, однако это может сделать ваш размер кучи действительно большим и неэффективным. Альтернативой является использование оболочки над отображенным в память файлом. Преимущество файлов, отображаемых в память, состоит в том, что они оказывают очень незначительное влияние на кучу и могут достаточно прозрачно загружаться и выгружаться ОС.
Огромная матрица
Этот код поддерживает большие матрицы double. Он разбивает файл на 1 ГБ сопоставления. (Поскольку Java не поддерживает сопоставления размером 2 ГБ и более одновременно, моя любимая ненависть;)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
import sun.misc.Cleaner; import sun.nio.ch.DirectBuffer; import java.io.Closeable; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.List; public class LargeDoubleMatrix implements Closeable { private static final int MAPPING_SIZE = 1 << 30 ; private final RandomAccessFile raf; private final int width; private final int height; private final List mappings = new ArrayList(); public LargeDoubleMatrix(String filename, int width, int height) throws IOException { this .raf = new RandomAccessFile(filename, "rw" ); try { this .width = width; this .height = height; long size = 8L * width * height; for ( long offset = 0 ; offset < size; offset += MAPPING_SIZE) { long size2 = Math.min(size - offset, MAPPING_SIZE); mappings.add(raf.getChannel().map(FileChannel.MapMode.READ_WRITE, offset, size2)); } } catch (IOException e) { raf.close(); throw e; } } protected long position( int x, int y) { return ( long ) y * width + x; } public int width() { return width; } public int height() { return height; } public double get( int x, int y) { assert x >= 0 && x < width; assert y >= 0 && y < height; long p = position(x, y) * 8 ; int mapN = ( int ) (p / MAPPING_SIZE); int offN = ( int ) (p % MAPPING_SIZE); return mappings.get(mapN).getDouble(offN); } public void set( int x, int y, double d) { assert x >= 0 && x < width; assert y >= 0 && y < height; long p = position(x, y) * 8 ; int mapN = ( int ) (p / MAPPING_SIZE); int offN = ( int ) (p % MAPPING_SIZE); mappings.get(mapN).putDouble(offN, d); } public void close() throws IOException { for (MappedByteBuffer mapping : mappings) clean(mapping); raf.close(); } private void clean(MappedByteBuffer mapping) { if (mapping == null ) return ; Cleaner cleaner = ((DirectBuffer) mapping).cleaner(); if (cleaner != null ) cleaner.clean(); } } public class LargeDoubleMatrixTest { @Test public void getSetMatrix() throws IOException { long start = System.nanoTime(); final long used0 = usedMemory(); LargeDoubleMatrix matrix = new LargeDoubleMatrix( "ldm.test" , 1000 * 1000 , 1000 * 1000 ); for ( int i = 0 ; i < matrix.width(); i++) matrix.set(i, i, i); for ( int i = 0 ; i < matrix.width(); i++) assertEquals(i, matrix.get(i, i), 0.0 ); long time = System.nanoTime() - start; final long used = usedMemory() - used0; if (used == 0 ) System.err.println( "You need to use -XX:-UseTLAB to see small changes in memory usage." ); System.out.printf( "Setting the diagonal took %,d ms, Heap used is %,d KB%n" , time / 1000 / 1000 , used / 1024 ); matrix.close(); } private long usedMemory() { return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); } } |
С помощью следующего теста, который записывает в каждое из диагональных значений матрицу миллион * миллион. Это слишком много, чтобы надеяться создать в куче.
1
2
3
4
5
6
|
Setting the diagonal took 314 , 819 ms, Heap used is 2 , 025 KB $ ls -l ldm.test -rw-rw-r-- 1 peter peter 8000000000000 2011 - 12 - 30 12 : 42 ldm.test $ du -s ldm.test 4010600 ldm.test |
Это 8 000 000 000 000 байтов или ~ 7,3 ТБ в виртуальной памяти в процессе Java! Это работает, потому что он только выделяет или страниц на страницах, которые вы используете. Таким образом, хотя размер файла составляет почти 8 ТБ, фактическое дисковое пространство и используемая память составляют 4 ГБ.
С более скромным размером файла матрицы 100K * 100K вы видите что-то вроде следующего. Это все еще матрица на 80 ГБ, которая использует тривиальное пространство кучи. ?
1
2
3
4
5
6
|
Setting the diagonal took 110 ms, Heap used is 71 KB $ ls -l ldm.test -rw-rw-r-- 1 peter peter 80000000000 2011 - 12 - 30 12 : 49 ldm.test $ du -s ldm.test 400000 ldm.test |
Ссылка: Использование файла отображения памяти для огромной матрицы от нашего партнера по JCG Питера Лоури из блога Vanilla Java
Статьи по Теме :
- Как получить C как производительность в Java
- Низкий GC в Java: используйте примитивы вместо оболочек
- Переработка объектов для улучшения производительности
- Советы по повышению производительности приложений Java
- Java Secret: загрузка и выгрузка статических полей
- Высокопроизводительный JPA с GlassFish и Coherence — Часть 1