SlideShare une entreprise Scribd logo
1  sur  39
Télécharger pour lire hors ligne
On-heap cache vs Off-heap cache
Radek Grębski
@RadekGrebski
https://github.com/rgrebski
PRESENTATION PLAN
1. Heap vs off-heap
2. Heap memory
3. Off-heap memory
3.1 Memory mapped file
3.2 Unsafe and ByteBuffers
3.3 Off-heap memory advantages
4. Cache:
4.1 Chronicle
4.2 Hazelcast
4.3 Redis
5. Comparison
1. HEAP VS OFF-HEAP
vs
2. HEAP MEMORY
JVM memory
JDK8
S1Eden S0 Old Generation
Heap (-Xmx)
Metaspace
(-XX:MaxMetaspaceSize)
Native memory
JVM Process memory
OS memory
Young Generation
Objects on heap
Source: http://www.ibm.com/developerworks/library/j-codetoheap/
Integer (64-bit JVM): 7:1 (28 bytes)
9:1 (36 bytes)
Integer (32-bit JVM):
3:1 overhead ratio
16 bytes
String (32-bit JVM):
3.75:1
60 bytes
int[1]
2. HEAP MEMORY
3. OFF-HEAP MEMORY
Off-heap (native) memoryJVM Process
OS memory
Other processes /
unallocated
byte[]
byte[]
3.1. MEMORY MAPPED FILE
Off-heap memoryJVM Process
OS memory
JVM Process
/tmp/myFile.dat
byte[]
byte[] byte[]
byte[]
3.2. UNSAFE AND BYTEBUFFERS
How to allocate memory using standard Java classes?
java.nio.ByteBuffer:
HeapByteBuffer (on-heap, up to 2gb)
DirectByteBuffer (off-heap, up to 2gb)
MappedByteBuffer (off-heap, up to 2gb, persisted)
sun.misc.Unsafe
public native long allocateMemory(long allocationSizeInBytes);
3.2. UNSAFE AND BYTEBUFFERS
ByteBuffer.allocate ( <2GB )
JvmUtils.verifyJvmArgumentsPresent("-Xmx2g");
ByteBuffer byteBuffer = ByteBuffer.allocate((int) ByteUtil.GB);
byteBuffer.putChar('a') //2bytes, position =0
.putInt(123) //4bytes, position = 2(0 + 2(char))
.put("test".getBytes("UTF-8")); //6 => 2(char) + 4(integer)
byte[] bytesToBeReadInto = new byte["test".getBytes("UTF-8").length];
char charA = byteBuffer.getChar(/*address*/ 0); // 'a'
int int123 = byteBuffer.getInt(/*address*/ 2); //123
byteBuffer.position(6); //set cursor position
byteBuffer.get(bytesToBeReadInto); //"test" as byte[] read into "bytesToBeReadIntoRead"
1. HEAP VS OFF-HEAP
vs
3.2. UNSAFE AND BYTEBUFFERS
Unallocating ByteBuffer memory
public static void callCleaner(ByteBuffer byteBuffer){
//DirectByteBuffer.cleaner().clean()
Method cleanerMethod = byteBuffer.getClass().getMethod("cleaner");
cleanerMethod.setAccessible(true);
Object cleaner = cleanerMethod.invoke(byteBuffer);
Method cleanMethod = cleaner.getClass().getMethod("clean");
cleanMethod.setAccessible(true);
cleanMethod.invoke(cleaner);
}
3.2. UNSAFE AND BYTEBUFFERS
Allocating memory using sun.misc.Unsafe
sun.misc.Unsafe::getUnsafe():
public static Unsafe getUnsafe() {
Class cc = sun.reflect.Reflection.getCallerClass(2);
if (cc.getClassLoader() != null)
throw new SecurityException("Unsafe");
return theUnsafe;
}
Creating an instance of Unsafe:
public static Unsafe createUnsafe() {
Constructor<Unsafe> unsafeConstructor = Unsafe.class.getDeclaredConstructor();
unsafeConstructor.setAccessible(true);
return unsafeConstructor.newInstance();
}
Memory allocation:
long memorySizeInBytes = ByteUtil.GB * 4;
long startAddress = unsafe.allocateMemory(memorySizeInBytes);
unsafe.setMemory(startAddress, memorySizeInBytes, (byte) 0);
unsafe.freeMemory(startAddress);
3.2. UNSAFE AND BYTEBUFFERS
Memory mapped file using Java API
File mappedFile = new File("/tmp/mappedFile.tmp");
mappedFile.delete();
try (FileChannel fileChannel = new RandomAccessFile(mappedFile, "rw").getChannel()) {
long buffer8MB = 8 * ByteUtil.MB;
MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0,
buffer8MB);
long startAddress = 0;
long elementsToPut = 200_000_000;
for (long counter = 0; counter < elementsToPut; counter++) {
if (!mappedByteBuffer.hasRemaining()) {
startAddress += mappedByteBuffer.position();
mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, startAddress,
buffer8MB);
}
mappedByteBuffer.putLong(counter);
}
}
Time: 1,068 s
Filesize: 1,49 GB
3.2. UNSAFE AND BYTEBUFFERS
Memory mapped file content
3.2. UNSAFE AND BYTEBUFFERS
Big endian vs Little endian
0xCAFEBABE
Address 00 01 02 03
Big endian CA FE BA BE
Address 00 01 02 03
Little endian BE BA FE CA
3.2. UNSAFE AND BYTEBUFFERS
ByteBuffer endianess
@Test
public void testEndianess() throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException {
ByteBuffer bigEndianByteBuffer = ByteBuffer.allocate(4);
bigEndianByteBuffer.order(ByteOrder.BIG_ENDIAN);
ByteBuffer littleEndianByteBuffer = ByteBuffer.allocate(4);
littleEndianByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
littleEndianByteBuffer.putInt(0xCAFEBABE);
bigEndianByteBuffer.putInt(0xCAFEBABE);
String bigEndianHexString = Hex.encodeHexString(bigEndianByteBuffer.array());
String littleEndianHexString = Hex.encodeHexString(littleEndianByteBuffer.array());
assertThat(bigEndianHexString).isEqualToIgnoringCase("CAFEBABE");
assertThat(littleEndianHexString).isEqualToIgnoringCase("BEBAFECA");
}
3.3. OFF-HEAP MEMORY ADVANTAGES
Off-heap storage
Pros and cons
No GC!
Manual GC!
Memory leaks
Persistence
IPC
Latency
Durability
Scalability (1TB+)
4. CACHE
HashMap
Collections.synchronizedMap(..)
ConcurrentHashMap
4.1. CHRONICLE
http://chronicle.software
Chronicle Map Chronicle Set Chronicle Queue
Chronicle Logger Java Thread Affinity
2. HEAP MEMORY
JVM memory
JDK8
S1Eden S0 Old Generation
Heap (-Xmx)
Metaspace
(-XX:MaxMetaspaceSize)
Native memory
JVM Process memory
OS memory
Young Generation
4.1. CHRONICLE MAP
Creating off-heap map instance
private Map<String, OffHeapUser> chronicleMap;
private static final String KEY_SAMPLE = "12345678901234567890";
private static final long MAX_ENTRIES = 10_000_000;
{
chronicleMap = ChronicleMapBuilder.of(String.class, OffHeapUser.class)
.averageKeySize(KEY_SAMPLE.getBytes("UTF-8").length)
.constantValueSizeBySample(new OffHeapUserSample())
.createPersistedTo(new File("/tmp/mappedFile.bin"))
.entries((long) (MAX_ENTRIES))
.create();
}
4.1. CHRONICLE MAP
Complex structures
public interface OffHeapUser {
String getUsername();
void setUsername(@MaxSize(30) String username);
long getAccountValidUntil();
void setAccountValidUntil(long accountValidUntil);
void setRoleAt(@MaxSize(2) int index, Role role);
Role getRoleAt(@MaxSize(2) int index);
static interface Role {
String getRole();
void setRole(@MaxSize(10) String role);
}
}
4.1. CHRONICLE MAP
Creating value instance
//fill the data
long accountValidUntil = System.currentTimeMillis() + YEAR_IN_MILLIS;
String username = RandomStringUtils.randomAlphabetic(20);
OffHeapUser offHeapUser = chronicleMap.newValueInstance();
offHeapUser.setAccountValidUntil(accountValidUntil);
offHeapUser.setUsername(username);
OffHeapUser.Role role0 = offHeapUser.getRoleAt(0);
role0.setRole("Role0");
OffHeapUser.Role role1 = offHeapUser.getRoleAt(1);
role0.setRole("Role1");
//put
chronicleMap.put("someKey", offHeapUser);
//get
OffHeapUser offHeapUserActual = chronicleMap.get("someKey");
4.1. CHRONICLE MAP
Throughput
Key = „u:0123456789”, value = counter
*ChronicleMap was tested with a 32 MB heap, CHM was test with a 100 GB heap.
Source: https://github.com/OpenHFT/Chronicle-Map
0
20
40
60
80
100
120
140
160
180
10 000 000 50 000 000 250 000 000 1 250 000 000
ThroughputMupd/s
Map entries
Throughput - ChronicleMap vs ConcurrentHashMap
Cronicle Map
ConcurrentHashMap
OutOfMemory
4.1. CHRONICLE MAP
Memory usage
OutOfMemory
0,0
20,0
40,0
60,0
80,0
100,0
120,0
140,0
10 000 000 50 000 000 250 000 000 1 250 000 000
MemoryinGB
Map entries
Memory used - ChronicleMap vs ConcurrentHashMap
Cronicle Map
ConcurrentHashMap
Key = „u:0123456789”, value = counter
*ChronicleMap was tested with a 32 MB heap, CHM was test with a 100 GB heap.
Source: https://github.com/OpenHFT/Chronicle-Map
4.1. CHRONICLE MAP
GC pauses
0,0
5,0
10,0
15,0
20,0
25,0
30,0
35,0
40,0
45,0
50,0
10 000 000 50 000 000 250 000 000 1 250 000 000
WorstGCpauseinseconds
Map entries
Worst GC pause [s] - ChronicleMap vs ConcurrentHashMap
Cronicle Map
ConcurrentHashMap
OutOfMemory
Key = „u:0123456789”, value = counter
*ChronicleMap was tested with a 32 MB heap, CHM was test with a 100 GB heap.
Source: https://github.com/OpenHFT/Chronicle-Map
Objects on heap
Source: http://www.ibm.com/developerworks/library/j-codetoheap/
Integer (64-bit JVM): 7:1 (28 bytes)
9:1 (36 bytes)
Integer (32-bit JVM):
3:1 overhead ratio
16 bytes
String (32-bit JVM):
3.75:1
60 bytes
int[1]
2. HEAP MEMORY
4.2. HAZELCAST
On-heap cache
Off-heap support (High Density Memory - commercial)
List, Map, Set, Queue
Topics
Executor Service
Dynamic clustering
Transactional
4.2. HAZELCAST
Example
@Test
public void hazelcastClusterTest(){
Config hazelcastConfig = new Config();
HazelcastInstance hazelcastInstance1 = Hazelcast.newHazelcastInstance(hazelcastConfig);
HazelcastInstance hazelcastInstance2 = Hazelcast.newHazelcastInstance(hazelcastConfig);
Map<String, String> node1Map = hazelcastInstance1.getMap("someMapName");
Map<String, String> node2Map = hazelcastInstance2.getMap("someMapName");
node1Map.put("key", "value");
Assertions.assertThat(node2Map.get("key")).isEqualTo("value");
}
Output:
Members [2] {
Member [192.168.1.23]:5701 this
Member [192.168.1.23]:5702
}
4.3. REDIS
REmote DIctionary Server
Key/Value cache + store
ANSI C
Used by:
StackOverflow
GitHub
Twitter
Instagram
Alibaba
Clients for almost all programming languages
4.3. REDIS
Redis data types
HashMaps (hset, hget)
LinkedList (lpush, ltrim, lrange)
Queue (lpush, rpop, brpop)
Topics (publish, subscribe)
Set (sadd, srem)
SortedSet (zadd, zrem)
4.3. REDIS
Redis Java clients
Jedis
Redisson
Aredis
JDBC-Redis
Jredis
Lettuce
RedisClient
4.3. REDIS
Redis HashMap example
@Test
public void testRedisMap(){
Jedis jedis = new Jedis("localhost");
// Pipeline pipeline = jedis.pipelined();
// pipeline.multi();
jedis.hset(/*map name*/ "user:1", /*key*/ "firstName", /*value*/"Radek");
jedis.hset("user:1", "lastName", "Grebski");
jedis.hset("user:1", "email", "rgrebski@gmail.com");
// pipeline.sync()
Map<String, String> mapFromRedis = jedis.hgetAll("user:1");
assertThat(jedis.hget("user:1", "firstName")).isEqualTo("Radek");
assertThat(mapFromRedis)
.hasSize(3)
.contains(entry("firstName", "Radek"))
.contains(entry("lastName", "Grebski"))
.contains(entry("email", "rgrebski@gmail.com"));
}
5. COMPARISON
Chronicle Hazelcast Redis
Deployment model Embedded Embedded / Separate Separate
Replication Yes Yes Master/Slave
Topics (pub/sub) No Yes Yes
Executor Service No Yes No
Persistence Yes (Memory mapped file) Yes (db, file, custom) Yes (periodically)
Java collections support Yes Yes Yes (Redisson)
Clustering No Yes Yes (since 3.0)
Distributed events No Yes Yes
Features
5. COMPARISON
R/W Performance
Chronicle Hazelcast Redis ConcurrentHashMap
Write (2mln entries) 2 555 583 178 491 110 668 1 100 715
Read (2mln entries) 937 207 214 638 156 177 4 092 769
0
500 000
1 000 000
1 500 000
2 000 000
2 500 000
3 000 000
3 500 000
4 000 000
4 500 000
Ops/s
R/W Performance
Heap: 1gb, G1 GC, Threads: 2, Records: 2mln, Entries: counter, User(username, 2x role, long)
5. PERFORMANCE COMPARISON
GC Pauses
Heap: 1gb, G1 GC, Threads: 2, Records: 2mln, Entries: counter, User(username, 2x role, long)
Chronicle Hazelcast Redis ConcurrentHashMap
GC pauses summarized 39 1452 480 726
GC pause max 8 28 15 360
0
200
400
600
800
1000
1200
1400
1600
Timeinmillis
Which cache should I use in my app ?
Questions?
3. OFF-HEAP MEMORY
Off-heap (native) memoryJVM Process
OS memory
Other processes /
unallocated
byte[]
byte[]

Contenu connexe

Tendances

Tendances (20)

20181116 Massive Log Processing using I/O optimized PostgreSQL
20181116 Massive Log Processing using I/O optimized PostgreSQL20181116 Massive Log Processing using I/O optimized PostgreSQL
20181116 Massive Log Processing using I/O optimized PostgreSQL
 
PG-Strom - GPGPU meets PostgreSQL, PGcon2015
PG-Strom - GPGPU meets PostgreSQL, PGcon2015PG-Strom - GPGPU meets PostgreSQL, PGcon2015
PG-Strom - GPGPU meets PostgreSQL, PGcon2015
 
Deep Learning for Developers
Deep Learning for DevelopersDeep Learning for Developers
Deep Learning for Developers
 
20181016_pgconfeu_ssd2gpu_multi
20181016_pgconfeu_ssd2gpu_multi20181016_pgconfeu_ssd2gpu_multi
20181016_pgconfeu_ssd2gpu_multi
 
SQL+GPU+SSD=∞ (English)
SQL+GPU+SSD=∞ (English)SQL+GPU+SSD=∞ (English)
SQL+GPU+SSD=∞ (English)
 
20170602_OSSummit_an_intelligent_storage
20170602_OSSummit_an_intelligent_storage20170602_OSSummit_an_intelligent_storage
20170602_OSSummit_an_intelligent_storage
 
第11回 配信講義 計算科学技術特論A(2021)
第11回 配信講義 計算科学技術特論A(2021)第11回 配信講義 計算科学技術特論A(2021)
第11回 配信講義 計算科学技術特論A(2021)
 
pgconfasia2016 plcuda en
pgconfasia2016 plcuda enpgconfasia2016 plcuda en
pgconfasia2016 plcuda en
 
[BGOUG] Java GC - Friend or Foe
[BGOUG] Java GC - Friend or Foe[BGOUG] Java GC - Friend or Foe
[BGOUG] Java GC - Friend or Foe
 
System Interconnects for HPC
System Interconnects for HPCSystem Interconnects for HPC
System Interconnects for HPC
 
Applying Recursive Temporal Blocking for Stencil Computations to Deeper Memor...
Applying Recursive Temporal Blocking for Stencil Computations to Deeper Memor...Applying Recursive Temporal Blocking for Stencil Computations to Deeper Memor...
Applying Recursive Temporal Blocking for Stencil Computations to Deeper Memor...
 
GTC Japan 2014
GTC Japan 2014GTC Japan 2014
GTC Japan 2014
 
Applying of the NVIDIA CUDA to the video processing in the task of the roundw...
Applying of the NVIDIA CUDA to the video processing in the task of the roundw...Applying of the NVIDIA CUDA to the video processing in the task of the roundw...
Applying of the NVIDIA CUDA to the video processing in the task of the roundw...
 
Accelerating microbiome research with OpenACC
Accelerating microbiome research with OpenACCAccelerating microbiome research with OpenACC
Accelerating microbiome research with OpenACC
 
Tokyo Cabinet & Tokyo Tyrant
Tokyo Cabinet & Tokyo TyrantTokyo Cabinet & Tokyo Tyrant
Tokyo Cabinet & Tokyo Tyrant
 
20160407_GTC2016_PgSQL_In_Place
20160407_GTC2016_PgSQL_In_Place20160407_GTC2016_PgSQL_In_Place
20160407_GTC2016_PgSQL_In_Place
 
20150318-SFPUG-Meetup-PGStrom
20150318-SFPUG-Meetup-PGStrom20150318-SFPUG-Meetup-PGStrom
20150318-SFPUG-Meetup-PGStrom
 
Chainer ui v0.3 and imagereport
Chainer ui v0.3 and imagereportChainer ui v0.3 and imagereport
Chainer ui v0.3 and imagereport
 
Xdp and ebpf_maps
Xdp and ebpf_mapsXdp and ebpf_maps
Xdp and ebpf_maps
 
CuPy v4 and v5 roadmap
CuPy v4 and v5 roadmapCuPy v4 and v5 roadmap
CuPy v4 and v5 roadmap
 

En vedette

Agg.eclipse.sequence.st.john
Agg.eclipse.sequence.st.johnAgg.eclipse.sequence.st.john
Agg.eclipse.sequence.st.john
Otis Armstrong
 
Laporan hp keusahawanan pengurusan
Laporan hp keusahawanan pengurusanLaporan hp keusahawanan pengurusan
Laporan hp keusahawanan pengurusan
salimomo
 

En vedette (20)

JDD2015: Sustainability Supporting Data Variability: Keeping Core Components ...
JDD2015: Sustainability Supporting Data Variability: Keeping Core Components ...JDD2015: Sustainability Supporting Data Variability: Keeping Core Components ...
JDD2015: Sustainability Supporting Data Variability: Keeping Core Components ...
 
JDD2015: Logowanie zdarzeń w architekturze opartej na mikroserwisach - Paweł ...
JDD2015: Logowanie zdarzeń w architekturze opartej na mikroserwisach - Paweł ...JDD2015: Logowanie zdarzeń w architekturze opartej na mikroserwisach - Paweł ...
JDD2015: Logowanie zdarzeń w architekturze opartej na mikroserwisach - Paweł ...
 
JDD2015: Z czym mierzą się zespoły? - Michał Bartyzel
JDD2015: Z czym mierzą się zespoły? - Michał Bartyzel JDD2015: Z czym mierzą się zespoły? - Michał Bartyzel
JDD2015: Z czym mierzą się zespoły? - Michał Bartyzel
 
JDD2015: -XX:+UseG1GC - Jakub Kubryński
JDD2015: -XX:+UseG1GC - Jakub KubryńskiJDD2015: -XX:+UseG1GC - Jakub Kubryński
JDD2015: -XX:+UseG1GC - Jakub Kubryński
 
JDD2015: Refactoring meets big money - Michał Gruca
JDD2015: Refactoring meets big money - Michał GrucaJDD2015: Refactoring meets big money - Michał Gruca
JDD2015: Refactoring meets big money - Michał Gruca
 
JDD2015: Thorny path to Data Mining projects - Alexey Zinoviev
JDD2015: Thorny path to Data Mining projects - Alexey Zinoviev JDD2015: Thorny path to Data Mining projects - Alexey Zinoviev
JDD2015: Thorny path to Data Mining projects - Alexey Zinoviev
 
PLNOG15-DNS is the root of all evil in the network. How to become a superhero...
PLNOG15-DNS is the root of all evil in the network. How to become a superhero...PLNOG15-DNS is the root of all evil in the network. How to become a superhero...
PLNOG15-DNS is the root of all evil in the network. How to become a superhero...
 
DevOpsDays Warsaw 2015: Deployment automation - what the naked eye do not see...
DevOpsDays Warsaw 2015: Deployment automation - what the naked eye do not see...DevOpsDays Warsaw 2015: Deployment automation - what the naked eye do not see...
DevOpsDays Warsaw 2015: Deployment automation - what the naked eye do not see...
 
PLNOG15 :Three faces of SDN - ACI vs NSX vs Nuage, Maciej Lelusz,Jarosław Zie...
PLNOG15 :Three faces of SDN - ACI vs NSX vs Nuage, Maciej Lelusz,Jarosław Zie...PLNOG15 :Three faces of SDN - ACI vs NSX vs Nuage, Maciej Lelusz,Jarosław Zie...
PLNOG15 :Three faces of SDN - ACI vs NSX vs Nuage, Maciej Lelusz,Jarosław Zie...
 
PLNOG16: Automatyzacja tworzenia sieci w środowisku Vmware, Maciej Lelusz
PLNOG16:  Automatyzacja tworzenia sieci w środowisku Vmware, Maciej LeluszPLNOG16:  Automatyzacja tworzenia sieci w środowisku Vmware, Maciej Lelusz
PLNOG16: Automatyzacja tworzenia sieci w środowisku Vmware, Maciej Lelusz
 
Hp 3
Hp 3Hp 3
Hp 3
 
Hascup
HascupHascup
Hascup
 
Agg.eclipse.sequence.st.john
Agg.eclipse.sequence.st.johnAgg.eclipse.sequence.st.john
Agg.eclipse.sequence.st.john
 
Laporan hp 2
Laporan hp 2Laporan hp 2
Laporan hp 2
 
Laporan hp 5 full
Laporan hp 5 fullLaporan hp 5 full
Laporan hp 5 full
 
Hp 5
Hp 5Hp 5
Hp 5
 
Hp4
Hp4Hp4
Hp4
 
Laporan hp 5
Laporan hp 5Laporan hp 5
Laporan hp 5
 
Laporan hp keusahawanan pengurusan
Laporan hp keusahawanan pengurusanLaporan hp keusahawanan pengurusan
Laporan hp keusahawanan pengurusan
 
Hp 3
Hp 3Hp 3
Hp 3
 

Similaire à JDD2015: On-heap cache vs Off-heap cache - Radek Grębski

Writing a TSDB from scratch_ performance optimizations.pdf
Writing a TSDB from scratch_ performance optimizations.pdfWriting a TSDB from scratch_ performance optimizations.pdf
Writing a TSDB from scratch_ performance optimizations.pdf
RomanKhavronenko
 
How to Stop Worrying and Start Caching in Java
How to Stop Worrying and Start Caching in JavaHow to Stop Worrying and Start Caching in Java
How to Stop Worrying and Start Caching in Java
srisatish ambati
 

Similaire à JDD2015: On-heap cache vs Off-heap cache - Radek Grębski (20)

Think Distributed: The Hazelcast Way
Think Distributed: The Hazelcast WayThink Distributed: The Hazelcast Way
Think Distributed: The Hazelcast Way
 
Distributed caching and computing v3.7
Distributed caching and computing v3.7Distributed caching and computing v3.7
Distributed caching and computing v3.7
 
Java и Linux — особенности эксплуатации / Алексей Рагозин (Дойче Банк)
Java и Linux — особенности эксплуатации / Алексей Рагозин (Дойче Банк)Java и Linux — особенности эксплуатации / Алексей Рагозин (Дойче Банк)
Java и Linux — особенности эксплуатации / Алексей Рагозин (Дойче Банк)
 
Mastering java in containers - MadridJUG
Mastering java in containers - MadridJUGMastering java in containers - MadridJUG
Mastering java in containers - MadridJUG
 
Writing a TSDB from scratch_ performance optimizations.pdf
Writing a TSDB from scratch_ performance optimizations.pdfWriting a TSDB from scratch_ performance optimizations.pdf
Writing a TSDB from scratch_ performance optimizations.pdf
 
Flink Forward Berlin 2017: Robert Metzger - Keep it going - How to reliably a...
Flink Forward Berlin 2017: Robert Metzger - Keep it going - How to reliably a...Flink Forward Berlin 2017: Robert Metzger - Keep it going - How to reliably a...
Flink Forward Berlin 2017: Robert Metzger - Keep it going - How to reliably a...
 
7 jvm-arguments-v1
7 jvm-arguments-v17 jvm-arguments-v1
7 jvm-arguments-v1
 
Node Interactive Debugging Node.js In Production
Node Interactive Debugging Node.js In ProductionNode Interactive Debugging Node.js In Production
Node Interactive Debugging Node.js In Production
 
Metrics: where and how
Metrics: where and howMetrics: where and how
Metrics: where and how
 
Всеволод Поляков (DevOps Team Lead в Grammarly)
Всеволод Поляков (DevOps Team Lead в Grammarly)Всеволод Поляков (DevOps Team Lead в Grammarly)
Всеволод Поляков (DevOps Team Lead в Grammarly)
 
mod-geocache / mapcache - A fast tiling solution for the apache web server
mod-geocache / mapcache - A fast tiling solution for the apache web servermod-geocache / mapcache - A fast tiling solution for the apache web server
mod-geocache / mapcache - A fast tiling solution for the apache web server
 
Distributed caching-computing v3.8
Distributed caching-computing v3.8Distributed caching-computing v3.8
Distributed caching-computing v3.8
 
Real-Time Big Data with Storm, Kafka and GigaSpaces
Real-Time Big Data with Storm, Kafka and GigaSpacesReal-Time Big Data with Storm, Kafka and GigaSpaces
Real-Time Big Data with Storm, Kafka and GigaSpaces
 
How to Stop Worrying and Start Caching in Java
How to Stop Worrying and Start Caching in JavaHow to Stop Worrying and Start Caching in Java
How to Stop Worrying and Start Caching in Java
 
CellCoverage
CellCoverageCellCoverage
CellCoverage
 
CRAMM: Virtual Memory Support for Garbage-Collected Applications
CRAMM: Virtual Memory Support for Garbage-Collected ApplicationsCRAMM: Virtual Memory Support for Garbage-Collected Applications
CRAMM: Virtual Memory Support for Garbage-Collected Applications
 
Heapoff memory wtf
Heapoff memory wtfHeapoff memory wtf
Heapoff memory wtf
 
Java In-Process Caching - Performance, Progress and Pittfalls
Java In-Process Caching - Performance, Progress and PittfallsJava In-Process Caching - Performance, Progress and Pittfalls
Java In-Process Caching - Performance, Progress and Pittfalls
 
Java In-Process Caching - Performance, Progress and Pitfalls
Java In-Process Caching - Performance, Progress and PitfallsJava In-Process Caching - Performance, Progress and Pitfalls
Java In-Process Caching - Performance, Progress and Pitfalls
 
Devoxx France 2018 : Mes Applications en Production sur Kubernetes
Devoxx France 2018 : Mes Applications en Production sur KubernetesDevoxx France 2018 : Mes Applications en Production sur Kubernetes
Devoxx France 2018 : Mes Applications en Production sur Kubernetes
 

Dernier

CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
9953056974 Low Rate Call Girls In Saket, Delhi NCR
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
masabamasaba
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
masabamasaba
 

Dernier (20)

CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
%+27788225528 love spells in Colorado Springs Psychic Readings, Attraction sp...
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
AI & Machine Learning Presentation Template
AI & Machine Learning Presentation TemplateAI & Machine Learning Presentation Template
AI & Machine Learning Presentation Template
 
The Top App Development Trends Shaping the Industry in 2024-25 .pdf
The Top App Development Trends Shaping the Industry in 2024-25 .pdfThe Top App Development Trends Shaping the Industry in 2024-25 .pdf
The Top App Development Trends Shaping the Industry in 2024-25 .pdf
 
Architecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the pastArchitecture decision records - How not to get lost in the past
Architecture decision records - How not to get lost in the past
 
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
%+27788225528 love spells in Atlanta Psychic Readings, Attraction spells,Brin...
 
Exploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdfExploring the Best Video Editing App.pdf
Exploring the Best Video Editing App.pdf
 
SHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions PresentationSHRMPro HRMS Software Solutions Presentation
SHRMPro HRMS Software Solutions Presentation
 
Define the academic and professional writing..pdf
Define the academic and professional writing..pdfDefine the academic and professional writing..pdf
Define the academic and professional writing..pdf
 
Software Quality Assurance Interview Questions
Software Quality Assurance Interview QuestionsSoftware Quality Assurance Interview Questions
Software Quality Assurance Interview Questions
 
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) SolutionIntroducing Microsoft’s new Enterprise Work Management (EWM) Solution
Introducing Microsoft’s new Enterprise Work Management (EWM) Solution
 
8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students8257 interfacing 2 in microprocessor for btech students
8257 interfacing 2 in microprocessor for btech students
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare%in Harare+277-882-255-28 abortion pills for sale in Harare
%in Harare+277-882-255-28 abortion pills for sale in Harare
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
Microsoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdfMicrosoft AI Transformation Partner Playbook.pdf
Microsoft AI Transformation Partner Playbook.pdf
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 

JDD2015: On-heap cache vs Off-heap cache - Radek Grębski

  • 1. On-heap cache vs Off-heap cache Radek Grębski @RadekGrebski https://github.com/rgrebski
  • 2. PRESENTATION PLAN 1. Heap vs off-heap 2. Heap memory 3. Off-heap memory 3.1 Memory mapped file 3.2 Unsafe and ByteBuffers 3.3 Off-heap memory advantages 4. Cache: 4.1 Chronicle 4.2 Hazelcast 4.3 Redis 5. Comparison
  • 3. 1. HEAP VS OFF-HEAP vs
  • 4. 2. HEAP MEMORY JVM memory JDK8 S1Eden S0 Old Generation Heap (-Xmx) Metaspace (-XX:MaxMetaspaceSize) Native memory JVM Process memory OS memory Young Generation
  • 5. Objects on heap Source: http://www.ibm.com/developerworks/library/j-codetoheap/ Integer (64-bit JVM): 7:1 (28 bytes) 9:1 (36 bytes) Integer (32-bit JVM): 3:1 overhead ratio 16 bytes String (32-bit JVM): 3.75:1 60 bytes int[1] 2. HEAP MEMORY
  • 6. 3. OFF-HEAP MEMORY Off-heap (native) memoryJVM Process OS memory Other processes / unallocated byte[] byte[]
  • 7. 3.1. MEMORY MAPPED FILE Off-heap memoryJVM Process OS memory JVM Process /tmp/myFile.dat byte[] byte[] byte[] byte[]
  • 8. 3.2. UNSAFE AND BYTEBUFFERS How to allocate memory using standard Java classes? java.nio.ByteBuffer: HeapByteBuffer (on-heap, up to 2gb) DirectByteBuffer (off-heap, up to 2gb) MappedByteBuffer (off-heap, up to 2gb, persisted) sun.misc.Unsafe public native long allocateMemory(long allocationSizeInBytes);
  • 9. 3.2. UNSAFE AND BYTEBUFFERS ByteBuffer.allocate ( <2GB ) JvmUtils.verifyJvmArgumentsPresent("-Xmx2g"); ByteBuffer byteBuffer = ByteBuffer.allocate((int) ByteUtil.GB); byteBuffer.putChar('a') //2bytes, position =0 .putInt(123) //4bytes, position = 2(0 + 2(char)) .put("test".getBytes("UTF-8")); //6 => 2(char) + 4(integer) byte[] bytesToBeReadInto = new byte["test".getBytes("UTF-8").length]; char charA = byteBuffer.getChar(/*address*/ 0); // 'a' int int123 = byteBuffer.getInt(/*address*/ 2); //123 byteBuffer.position(6); //set cursor position byteBuffer.get(bytesToBeReadInto); //"test" as byte[] read into "bytesToBeReadIntoRead"
  • 10. 1. HEAP VS OFF-HEAP vs
  • 11. 3.2. UNSAFE AND BYTEBUFFERS Unallocating ByteBuffer memory public static void callCleaner(ByteBuffer byteBuffer){ //DirectByteBuffer.cleaner().clean() Method cleanerMethod = byteBuffer.getClass().getMethod("cleaner"); cleanerMethod.setAccessible(true); Object cleaner = cleanerMethod.invoke(byteBuffer); Method cleanMethod = cleaner.getClass().getMethod("clean"); cleanMethod.setAccessible(true); cleanMethod.invoke(cleaner); }
  • 12. 3.2. UNSAFE AND BYTEBUFFERS Allocating memory using sun.misc.Unsafe sun.misc.Unsafe::getUnsafe(): public static Unsafe getUnsafe() { Class cc = sun.reflect.Reflection.getCallerClass(2); if (cc.getClassLoader() != null) throw new SecurityException("Unsafe"); return theUnsafe; } Creating an instance of Unsafe: public static Unsafe createUnsafe() { Constructor<Unsafe> unsafeConstructor = Unsafe.class.getDeclaredConstructor(); unsafeConstructor.setAccessible(true); return unsafeConstructor.newInstance(); } Memory allocation: long memorySizeInBytes = ByteUtil.GB * 4; long startAddress = unsafe.allocateMemory(memorySizeInBytes); unsafe.setMemory(startAddress, memorySizeInBytes, (byte) 0); unsafe.freeMemory(startAddress);
  • 13. 3.2. UNSAFE AND BYTEBUFFERS Memory mapped file using Java API File mappedFile = new File("/tmp/mappedFile.tmp"); mappedFile.delete(); try (FileChannel fileChannel = new RandomAccessFile(mappedFile, "rw").getChannel()) { long buffer8MB = 8 * ByteUtil.MB; MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, buffer8MB); long startAddress = 0; long elementsToPut = 200_000_000; for (long counter = 0; counter < elementsToPut; counter++) { if (!mappedByteBuffer.hasRemaining()) { startAddress += mappedByteBuffer.position(); mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, startAddress, buffer8MB); } mappedByteBuffer.putLong(counter); } } Time: 1,068 s Filesize: 1,49 GB
  • 14. 3.2. UNSAFE AND BYTEBUFFERS Memory mapped file content
  • 15. 3.2. UNSAFE AND BYTEBUFFERS Big endian vs Little endian 0xCAFEBABE Address 00 01 02 03 Big endian CA FE BA BE Address 00 01 02 03 Little endian BE BA FE CA
  • 16. 3.2. UNSAFE AND BYTEBUFFERS ByteBuffer endianess @Test public void testEndianess() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { ByteBuffer bigEndianByteBuffer = ByteBuffer.allocate(4); bigEndianByteBuffer.order(ByteOrder.BIG_ENDIAN); ByteBuffer littleEndianByteBuffer = ByteBuffer.allocate(4); littleEndianByteBuffer.order(ByteOrder.LITTLE_ENDIAN); littleEndianByteBuffer.putInt(0xCAFEBABE); bigEndianByteBuffer.putInt(0xCAFEBABE); String bigEndianHexString = Hex.encodeHexString(bigEndianByteBuffer.array()); String littleEndianHexString = Hex.encodeHexString(littleEndianByteBuffer.array()); assertThat(bigEndianHexString).isEqualToIgnoringCase("CAFEBABE"); assertThat(littleEndianHexString).isEqualToIgnoringCase("BEBAFECA"); }
  • 17. 3.3. OFF-HEAP MEMORY ADVANTAGES Off-heap storage Pros and cons No GC! Manual GC! Memory leaks Persistence IPC Latency Durability Scalability (1TB+)
  • 19. 4.1. CHRONICLE http://chronicle.software Chronicle Map Chronicle Set Chronicle Queue Chronicle Logger Java Thread Affinity
  • 20. 2. HEAP MEMORY JVM memory JDK8 S1Eden S0 Old Generation Heap (-Xmx) Metaspace (-XX:MaxMetaspaceSize) Native memory JVM Process memory OS memory Young Generation
  • 21. 4.1. CHRONICLE MAP Creating off-heap map instance private Map<String, OffHeapUser> chronicleMap; private static final String KEY_SAMPLE = "12345678901234567890"; private static final long MAX_ENTRIES = 10_000_000; { chronicleMap = ChronicleMapBuilder.of(String.class, OffHeapUser.class) .averageKeySize(KEY_SAMPLE.getBytes("UTF-8").length) .constantValueSizeBySample(new OffHeapUserSample()) .createPersistedTo(new File("/tmp/mappedFile.bin")) .entries((long) (MAX_ENTRIES)) .create(); }
  • 22. 4.1. CHRONICLE MAP Complex structures public interface OffHeapUser { String getUsername(); void setUsername(@MaxSize(30) String username); long getAccountValidUntil(); void setAccountValidUntil(long accountValidUntil); void setRoleAt(@MaxSize(2) int index, Role role); Role getRoleAt(@MaxSize(2) int index); static interface Role { String getRole(); void setRole(@MaxSize(10) String role); } }
  • 23. 4.1. CHRONICLE MAP Creating value instance //fill the data long accountValidUntil = System.currentTimeMillis() + YEAR_IN_MILLIS; String username = RandomStringUtils.randomAlphabetic(20); OffHeapUser offHeapUser = chronicleMap.newValueInstance(); offHeapUser.setAccountValidUntil(accountValidUntil); offHeapUser.setUsername(username); OffHeapUser.Role role0 = offHeapUser.getRoleAt(0); role0.setRole("Role0"); OffHeapUser.Role role1 = offHeapUser.getRoleAt(1); role0.setRole("Role1"); //put chronicleMap.put("someKey", offHeapUser); //get OffHeapUser offHeapUserActual = chronicleMap.get("someKey");
  • 24. 4.1. CHRONICLE MAP Throughput Key = „u:0123456789”, value = counter *ChronicleMap was tested with a 32 MB heap, CHM was test with a 100 GB heap. Source: https://github.com/OpenHFT/Chronicle-Map 0 20 40 60 80 100 120 140 160 180 10 000 000 50 000 000 250 000 000 1 250 000 000 ThroughputMupd/s Map entries Throughput - ChronicleMap vs ConcurrentHashMap Cronicle Map ConcurrentHashMap OutOfMemory
  • 25. 4.1. CHRONICLE MAP Memory usage OutOfMemory 0,0 20,0 40,0 60,0 80,0 100,0 120,0 140,0 10 000 000 50 000 000 250 000 000 1 250 000 000 MemoryinGB Map entries Memory used - ChronicleMap vs ConcurrentHashMap Cronicle Map ConcurrentHashMap Key = „u:0123456789”, value = counter *ChronicleMap was tested with a 32 MB heap, CHM was test with a 100 GB heap. Source: https://github.com/OpenHFT/Chronicle-Map
  • 26. 4.1. CHRONICLE MAP GC pauses 0,0 5,0 10,0 15,0 20,0 25,0 30,0 35,0 40,0 45,0 50,0 10 000 000 50 000 000 250 000 000 1 250 000 000 WorstGCpauseinseconds Map entries Worst GC pause [s] - ChronicleMap vs ConcurrentHashMap Cronicle Map ConcurrentHashMap OutOfMemory Key = „u:0123456789”, value = counter *ChronicleMap was tested with a 32 MB heap, CHM was test with a 100 GB heap. Source: https://github.com/OpenHFT/Chronicle-Map
  • 27. Objects on heap Source: http://www.ibm.com/developerworks/library/j-codetoheap/ Integer (64-bit JVM): 7:1 (28 bytes) 9:1 (36 bytes) Integer (32-bit JVM): 3:1 overhead ratio 16 bytes String (32-bit JVM): 3.75:1 60 bytes int[1] 2. HEAP MEMORY
  • 28. 4.2. HAZELCAST On-heap cache Off-heap support (High Density Memory - commercial) List, Map, Set, Queue Topics Executor Service Dynamic clustering Transactional
  • 29. 4.2. HAZELCAST Example @Test public void hazelcastClusterTest(){ Config hazelcastConfig = new Config(); HazelcastInstance hazelcastInstance1 = Hazelcast.newHazelcastInstance(hazelcastConfig); HazelcastInstance hazelcastInstance2 = Hazelcast.newHazelcastInstance(hazelcastConfig); Map<String, String> node1Map = hazelcastInstance1.getMap("someMapName"); Map<String, String> node2Map = hazelcastInstance2.getMap("someMapName"); node1Map.put("key", "value"); Assertions.assertThat(node2Map.get("key")).isEqualTo("value"); } Output: Members [2] { Member [192.168.1.23]:5701 this Member [192.168.1.23]:5702 }
  • 30. 4.3. REDIS REmote DIctionary Server Key/Value cache + store ANSI C Used by: StackOverflow GitHub Twitter Instagram Alibaba Clients for almost all programming languages
  • 31. 4.3. REDIS Redis data types HashMaps (hset, hget) LinkedList (lpush, ltrim, lrange) Queue (lpush, rpop, brpop) Topics (publish, subscribe) Set (sadd, srem) SortedSet (zadd, zrem)
  • 32. 4.3. REDIS Redis Java clients Jedis Redisson Aredis JDBC-Redis Jredis Lettuce RedisClient
  • 33. 4.3. REDIS Redis HashMap example @Test public void testRedisMap(){ Jedis jedis = new Jedis("localhost"); // Pipeline pipeline = jedis.pipelined(); // pipeline.multi(); jedis.hset(/*map name*/ "user:1", /*key*/ "firstName", /*value*/"Radek"); jedis.hset("user:1", "lastName", "Grebski"); jedis.hset("user:1", "email", "rgrebski@gmail.com"); // pipeline.sync() Map<String, String> mapFromRedis = jedis.hgetAll("user:1"); assertThat(jedis.hget("user:1", "firstName")).isEqualTo("Radek"); assertThat(mapFromRedis) .hasSize(3) .contains(entry("firstName", "Radek")) .contains(entry("lastName", "Grebski")) .contains(entry("email", "rgrebski@gmail.com")); }
  • 34. 5. COMPARISON Chronicle Hazelcast Redis Deployment model Embedded Embedded / Separate Separate Replication Yes Yes Master/Slave Topics (pub/sub) No Yes Yes Executor Service No Yes No Persistence Yes (Memory mapped file) Yes (db, file, custom) Yes (periodically) Java collections support Yes Yes Yes (Redisson) Clustering No Yes Yes (since 3.0) Distributed events No Yes Yes Features
  • 35. 5. COMPARISON R/W Performance Chronicle Hazelcast Redis ConcurrentHashMap Write (2mln entries) 2 555 583 178 491 110 668 1 100 715 Read (2mln entries) 937 207 214 638 156 177 4 092 769 0 500 000 1 000 000 1 500 000 2 000 000 2 500 000 3 000 000 3 500 000 4 000 000 4 500 000 Ops/s R/W Performance Heap: 1gb, G1 GC, Threads: 2, Records: 2mln, Entries: counter, User(username, 2x role, long)
  • 36. 5. PERFORMANCE COMPARISON GC Pauses Heap: 1gb, G1 GC, Threads: 2, Records: 2mln, Entries: counter, User(username, 2x role, long) Chronicle Hazelcast Redis ConcurrentHashMap GC pauses summarized 39 1452 480 726 GC pause max 8 28 15 360 0 200 400 600 800 1000 1200 1400 1600 Timeinmillis
  • 37. Which cache should I use in my app ?
  • 39. 3. OFF-HEAP MEMORY Off-heap (native) memoryJVM Process OS memory Other processes / unallocated byte[] byte[]