-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Супрядкина Дарья ИТМО DWS stage 6 #218
Merged
Merged
Changes from all commits
Commits
Show all changes
79 commits
Select commit
Hold shift + click to select a range
2f2080a
HW1: add realization
SuDarina a18dc95
HW1: fix code style
SuDarina 528366b
HW1: change exception constructor
SuDarina 990d153
HW1: add report
SuDarina 3587983
HW1: add flame graphs description
SuDarina 75db05e
HW1: add flame graphs description
SuDarina 3c9655b
HW1: fix code style
SuDarina ad686bc
HW1: fixes according to review
SuDarina 1d5c62c
HW2: first version of HW2
SuDarina 825f223
HW2: fix codestyle
SuDarina 8fe320f
Merge branch 'main' into hw2/async-server
incubos c5a6710
Merge remote-tracking branch 'upstream/main' into hw2/async-server
SuDarina e6673e6
HW2: fixes according to review
SuDarina 474cc38
HW2: change string template to typical string
SuDarina c601bb6
HW2: change parameters
SuDarina fe0ffa5
HW2: fix
SuDarina f1da741
HW2: just finish
SuDarina 55458dc
Merge branch 'main' into hw2/async-server
incubos 7b834f5
HW2: fixes according to review
SuDarina 022c4d6
HW2: explaining absence of flush
SuDarina 7de6005
HW3: add realization
SuDarina 889bdd7
HW3: fix style
SuDarina c00be65
HW3: add final
SuDarina 69c9b6d
HW3: fix finals
SuDarina c852e2f
HW3: change to async thread
SuDarina 6e9031e
HW3: add shutdown threads
SuDarina 7529b7b
HW3: fix style
SuDarina 91ba923
Merge remote-tracking branch 'upstream/main' into hw3/sharding
SuDarina a9c58df
HW3: add first part of report
SuDarina 141a869
HW3: add full report
SuDarina 0f118ed
HW3: fix style
SuDarina afd4a3f
HW3: add explanations about hashCode()
SuDarina e9259b8
Merge remote-tracking branch 'upstream/main' into hw4/replication
SuDarina b4fae77
HW4: add implementation
SuDarina 9ffd458
HW4: fix style
SuDarina 6d47409
HW4: add report
SuDarina 4f529d4
Merge remote-tracking branch 'upstream/main' into hw4/replication
SuDarina ab42efe
HW4: fix code style
SuDarina 6625f89
HW4: add note
SuDarina a13db87
Merge branch 'main' into hw4/replication
0ed611c
HW5: add realization
SuDarina 6da7828
HW5: add completed future
SuDarina 80a406b
HW5: fix tolerance
SuDarina 9196595
Merge remote-tracking branch 'upstream/main' into hw5/async-interaction
SuDarina dc30ab3
HW5: fix codestyle
SuDarina 6fe0449
HW5: fix codestyle
SuDarina 629090b
HW5: close sessions
SuDarina 33f5c3a
HW5: rewrite to method sendResponseAndCloseSession
SuDarina 7523858
HW5: remove redundant parameter
SuDarina d0bd0e9
HW5: set close sessions
SuDarina 2ee3bd4
HW5: change
SuDarina 1db3665
HW5: change
SuDarina dca0f2d
Merge branch 'main' into hw5/async-interaction
incubos 9d65379
HW5: fix multithreading problem for better profiling
SuDarina 1d65f01
HW5: fix codestyle
SuDarina 6be3d39
HW5: add report
SuDarina b14ee15
Merge remote-tracking branch 'upstream/main' into hw6/range-requests
SuDarina 9910f2c
HW6: add range requests
SuDarina 0a1e84c
HW6: fix checkstyle
SuDarina c4b5a78
HW6: move methods
SuDarina dedc72c
Merge branch 'main' into hw6/range-requests
incubos 3a569d0
Merge branch 'main' into hw5/async-interaction
incubos f991781
HW6: add profiling results
SuDarina c680ff6
HW6: add first part of report
SuDarina 71b103d
Merge branch 'main' into hw5/async-interaction
incubos 2fb2d03
HW6: add report
SuDarina f7648f6
HW6: remarks
SuDarina c59f446
Merge branch 'main' into hw6/range-requests
incubos 595df0a
HW5: fixes according to review
SuDarina 2049803
HW5: fix header
SuDarina 3788b33
Merge branch 'main' into hw5/async-interaction
incubos 818a350
HW5: fixes according to review
SuDarina e9e93eb
Merge remote-tracking branch 'origin/hw5/async-interaction' into hw5/…
SuDarina b05df36
HW5: complete todo
SuDarina 8785bb5
Merge remote-tracking branch 'origin/hw5/async-interaction' into hw6/…
SuDarina 3f50aad
HW6: make not greedy realization
SuDarina 4559a65
Merge remote-tracking branch 'upstream/main' into hw6/range-requests
SuDarina f85f7b3
HW6: reduce unnecessary allocations
SuDarina 76d12f2
Merge branch 'main' into hw6/range-requests
incubos File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
50 changes: 50 additions & 0 deletions
50
src/main/java/ru/vk/itmo/test/dariasupriadkina/CustomHttpSession.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package ru.vk.itmo.test.dariasupriadkina; | ||
|
||
import one.nio.http.HttpServer; | ||
import one.nio.http.HttpSession; | ||
import one.nio.net.Socket; | ||
import one.nio.util.ByteArrayBuilder; | ||
import ru.vk.itmo.test.dariasupriadkina.dao.ExtendedEntry; | ||
|
||
import java.io.IOException; | ||
import java.lang.foreign.MemorySegment; | ||
import java.util.Iterator; | ||
|
||
public class CustomHttpSession extends HttpSession { | ||
|
||
private Iterator<ExtendedEntry<MemorySegment>> iterator; | ||
private ByteArrayBuilder builder; | ||
|
||
public CustomHttpSession(Socket socket, HttpServer server) { | ||
super(socket, server); | ||
} | ||
|
||
@Override | ||
protected void processWrite() throws Exception { | ||
super.processWrite(); | ||
|
||
sendNextChunkScope(builder == null ? new ByteArrayBuilder() : builder); | ||
} | ||
|
||
public void streaming(Iterator<ExtendedEntry<MemorySegment>> iterator) throws IOException { | ||
this.iterator = iterator; | ||
this.builder = new ByteArrayBuilder(); | ||
|
||
write(EntryChunkUtils.HEADER_BYTES, 0, EntryChunkUtils.HEADER_BYTES.length); | ||
sendNextChunkScope(builder); | ||
} | ||
|
||
private void sendNextChunkScope(ByteArrayBuilder builder) throws IOException { | ||
while (iterator.hasNext() && queueHead == null) { | ||
ExtendedEntry<MemorySegment> ee = iterator.next(); | ||
EntryChunkUtils.getEntryByteChunk(ee, builder); | ||
write(builder.buffer(), 0, builder.length()); | ||
builder.setLength(0); | ||
} | ||
if (!iterator.hasNext()) { | ||
write(EntryChunkUtils.LAST_BYTES, 0, EntryChunkUtils.LAST_BYTES.length); | ||
} | ||
scheduleClose(); | ||
} | ||
|
||
} |
53 changes: 53 additions & 0 deletions
53
src/main/java/ru/vk/itmo/test/dariasupriadkina/EntryChunkUtils.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package ru.vk.itmo.test.dariasupriadkina; | ||
|
||
import one.nio.util.ByteArrayBuilder; | ||
import ru.vk.itmo.dao.Entry; | ||
|
||
import java.lang.foreign.MemorySegment; | ||
import java.lang.foreign.ValueLayout; | ||
import java.nio.charset.StandardCharsets; | ||
|
||
public final class EntryChunkUtils { | ||
|
||
private EntryChunkUtils() { | ||
} | ||
|
||
static final byte[] HEADER_BYTES = | ||
""" | ||
HTTP/1.1 200 OK\r | ||
incubos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Content-Type: text/plain\r | ||
Transfer-Encoding: chunked\r | ||
Connection: keep-alive\r | ||
\r | ||
""".getBytes(StandardCharsets.UTF_8); | ||
static final byte[] LAST_BYTES = "0\r\n\r\n".getBytes(StandardCharsets.UTF_8); | ||
private static final byte[] DELIMITER_BYTES = "\n".getBytes(StandardCharsets.UTF_8); | ||
private static final byte[] CLRF_BYTES = "\r\n".getBytes(StandardCharsets.UTF_8); | ||
|
||
public static void getEntryByteChunk(Entry<MemorySegment> ee, ByteArrayBuilder bb) { | ||
byte[] key = getEntryKeyChunk(ee); | ||
byte[] value = getEntryValueChunk(ee); | ||
byte[] kvLength = getKVLengthChunk(ee); | ||
|
||
bb.append(kvLength, 0, kvLength.length); | ||
bb.append(CLRF_BYTES, 0, CLRF_BYTES.length); | ||
bb.append(key, 0, key.length); | ||
bb.append(DELIMITER_BYTES, 0, DELIMITER_BYTES.length); | ||
bb.append(value, 0, value.length); | ||
bb.append(CLRF_BYTES, 0, CLRF_BYTES.length); | ||
} | ||
|
||
public static byte[] getEntryKeyChunk(Entry<MemorySegment> entry) { | ||
return entry.key().toArray(ValueLayout.JAVA_BYTE); | ||
} | ||
|
||
public static byte[] getEntryValueChunk(Entry<MemorySegment> entry) { | ||
return entry.value().toArray(ValueLayout.JAVA_BYTE); | ||
} | ||
|
||
public static byte[] getKVLengthChunk(Entry<MemorySegment> entry) { | ||
return Long.toHexString((entry.value().byteSize() | ||
+ entry.key().byteSize() + DELIMITER_BYTES.length)).getBytes(StandardCharsets.UTF_8); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
101 changes: 101 additions & 0 deletions
101
src/main/java/ru/vk/itmo/test/dariasupriadkina/report/hw6/REPORT.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
# Range-запросы | ||
|
||
На начало проведения экспериментов база была заполнена на 777Мб, количество записей - ≈8_990_000 | ||
|
||
Снимем профили аллокации, блокировок и cpu, нагрузив систему командой curl, | ||
с помощью которой запросим все записи базы данных, начиная с 1ой: | ||
|
||
``` | ||
curl -v http://localhost:8080/v0/entities\?start=1 | ||
``` | ||
|
||
## Результаты профилирования | ||
|
||
[range-alloc.html](data%2Frange-alloc.html) | ||
|
||
[range-cpu.html](data%2Frange-cpu.html) | ||
|
||
[range-lock.html](data%2Frange-lock.html) | ||
|
||
|
||
На профиле блокировок видно, что практически 100% локов уходит на `Session.write` | ||
|
||
Если посмотреть на профиль cpu, то разделение сеймплов примерно такое: | ||
|
||
- `Session.write` - 48.7% | ||
- `getEntryByteChunk` (метод, отвечающий за формирование байтового чанка с данными) - 14.7%, что выглядит весьма солидными | ||
затратами для обычного преобразования `MemorySegment`'ов в массив байт | ||
- `MergingEntryIterator` (работа dao) - 28.7% | ||
- Оставшаяся часть - работа `SelectorThread` | ||
|
||
На профиле аллокаций дела обстоят несколько иначе: | ||
|
||
- `MergingEntryIterator.next` (работа dao) - 10.6% | ||
- `Session.write` - всего около 9% несмотря на то, что занимает больше всего процессорного времени | ||
- `getEntryByteChunk` - ≈45%. В рамках этого метода у нас по отдельности преобразуется в байтовый массив каждая из частей | ||
- `ByteArrayBuilder.<init>` - ≈25% - инициализация ByteArrayBuilder | ||
|
||
`getEntryByteChunk` забирает на себя практически половину всех аллокаций, что выглядит узким местом, которое нуждается | ||
в оптимизации | ||
|
||
`ByteArrayBuilder.<init>` также занимает весьма солидный процент аллокаций - целую четверть. Пичина возникновения этих алллокаций весьма очевидна, | ||
в коде мы аллоцируем новый ByteArrayBuilder каждый раз, когда формируем новый чанк key-value с данными | ||
|
||
``` | ||
while (it.hasNext()) { | ||
ByteArrayBuilder bb = new ByteArrayBuilder(); | ||
ExtendedEntry<MemorySegment> ee = it.next(); | ||
EntryChunkUtils.getEntryByteChunk(ee, bb); | ||
session.write(bb.toBytes(), 0, bb.length()); | ||
} | ||
``` | ||
|
||
Это решение было принято, чтобы отправлять через `session.write` весь чанк с данными, а не каждую его часть | ||
(length, crlf, <key>, \n, value) по отдельность. В качестве альтернативы можно как раз отправлять каждую часть через session.write, | ||
тогда аллокацию ByteArrayBuilder можно избежать, но, так как на профиле локов, фигурирует в основном только метод Session.write, | ||
вероятно, лишнее использование Session.write может привести к затратам на синхронизацию, отчего в конце концов пострадает latency | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. У нас в сессию пишет один поток, поэтому никакого contention нет и затраты на синхронизацию не изменятся. |
||
и сама производительность системы. | ||
|
||
Тем не менее имеет смысл судя по полученным результатам профилирования, имеет смысл попытаться оптимизировать процесс формирования | ||
массивов байт. | ||
|
||
Заметно, что наибольшие затраты в рамках `getEntryByteChunk` происходят в методе расчета длины чанка - `getKVLengthChank` | ||
(27.7%). | ||
В первоначальной реализации дл того, чтобы получить длину ключа и значения, `MemorySegment` приводился сначала к массиву | ||
байт, а затем бралась длина. С целью попытки оптимизации этого момента, вся цепочка преобразований была заменена на метод | ||
получения длины `MemorySegment`'а в байтах - `byteSize()`, также расчет длины символа `\n` была заменена с `"\n".length()` | ||
на `DELIMITER_BYTES.length`, где DELIMITER_BYTES - константа. Таким образом мы избегаем инициализации новой строчки. | ||
|
||
Даже такие небольшие изменения позволили уменьшить процент аллокаций в методе `getEntryByteChunk` с 45% до 33%, а в самом | ||
методе `getKVLengthChank` процент уменьшился с 27.7% до 11.17%. | ||
|
||
Новые результаты профилирования аллокаций: | ||
|
||
[range-alloc-2.html](data%2Frange-alloc-2.html) | ||
|
||
Как видно на профиле, при снижении процента аллокаций на работу с преобразованием `MemorySegment`'ов в массивы байт | ||
гораздо заметнее стали ресурсы, затрачиваемые на инициализацию `ByteArrayBuilder`, который инициализируется при формировании каждого | ||
чанка. В идеале подобных инициализаций в цикле нам явно хотелось бы избежать. Несильно уверена в следующем предположении, | ||
но, обратив внимание на принципы работы | ||
`ByteArrayBuilder`, предполагая, что вместо инициализации `ByteArrayBuilder` можно использовать метод `.setLength(0)`. | ||
|
||
В таком случае вышеупомянутый кусок кода можно заменить на: | ||
|
||
``` | ||
while (it.hasNext()) { | ||
ExtendedEntry<MemorySegment> ee = it.next(); | ||
EntryChunkUtils.getEntryByteChunk(ee, bb); | ||
session.write(bb.toBytes(), 0, bb.length()); | ||
incubos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
bb.setLength(0); | ||
} | ||
``` | ||
|
||
P.S. По крайней мере после таких изменений тесты по-прежнему проходят и curl работает точно так же, как и до этого изменения. | ||
|
||
Результаты профилирования последней версии: | ||
|
||
[range-alloc-3.html](data%2Frange-alloc-3.html) | ||
incubos marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
[range-cpu-3.html](data%2Frange-cpu-3.html) | ||
|
||
Если опять же обратиться к профилю аллокаций, то можно увидеть, что `ByteArrayBuilder.<init>` пропал совсем. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Мы могли не всё отправить, но всегда инициируем закрытие сессии, верно?