Every saga has a beginning...

  • в пике - 1000 подключений
  • С++ / потоки
  • утечки памяти
* thread #2: tid = 0x3103, 0x000000010003bf2b libcurl.dylib`Curl_pp_readresp(sockfd=4, pp=0x0000000100806508, code=0x0000000100780ad4, size=0x0000000100780b20) + 794 at pingpong.c:379, stop reason = EXC_BAD_ACCESS (code=1, address=0x102800000) 
    frame #0: 0x000000010003bf2b libcurl.dylib`Curl_pp_readresp(sockfd=4, pp=0x0000000100806508, code=0x0000000100780ad4, size=0x0000000100780b20) + 794 at pingpong.c:379 
    frame #1: 0x0000000100011e43 libcurl.dylib`http_readresp(, httpcode=0x0000000100780b8c) + 29 at http.c:627 
    frame #2: 0x0000000100011dab libcurl.dylib`Curl_GethttpResponse(nreadp=0x0000000100780b90, conn=0x0000000100806000) + 227 at http.c:759 
    frame #3: 0x00000001000115ec libcurl.dylib`http_done [inlined] http_sendquote(, conn=0x0000000100806000) + 86 at http.c:3530 
    frame #4: 0x0000000100011596 libcurl.dylib`http_done(conn=0x0000000100806000, status=CURLE_OK, premature=false) + 1258 at http.c:3485 
    frame #5: 0x000000010001c8ed libcurl.dylib`Curl_done(connp=0x0000000101a00038) + 271 at url.c:5425 
    frame #6: 0x0000000100029eae libcurl.dylib`multi_runsingle(multi=0x0000000100600000, now=(null), easy=0x0000000101a00020) + 1580 at multi.c:1577 
    frame #7: 0x000000010002b0ee libcurl.dylib`multi_socket(multi=0x0000000100600000, checkall=true, s=-1, running_handles=0x0000000100780d94) + 420 at multi.c:2194 
    frame #8: 0x000000010002b180 libcurl.dylib`curl_multi_socket_action(multi_handle=0x0000000100600000) + 27 at multi.c:2297 
    frame #9: 0x000000010000126a crashtest`curl_perform_action(socket=-1, actions=0) + 42 at multi-gcd-crashtest.c:101 
    frame #10: 0x000000010000209d crashtest`__create_timeout_block_invoke(.block_descriptor=0x0000000100003210) + 29 at multi-gcd-crashtest.c:167 
    frame #11: 0x00007fff995710b6 libdispatch.dylib`_dispatch_client_callout + 8 
    frame #12: 0x00007fff9957329b libdispatch.dylib`_dispatch_source_invoke + 691 
    frame #13: 0x00007fff99572305 libdispatch.dylib`_dispatch_queue_invoke + 72 
    frame #14: 0x00007fff99572448 libdispatch.dylib`_dispatch_queue_drain + 180 
    frame #15: 0x00007fff995722f1 libdispatch.dylib`_dispatch_queue_invoke + 52 
    frame #16: 0x00007fff995721c3 libdispatch.dylib`_dispatch_worker_thread2 + 249 
    frame #17: 0x00007fff99731d0b libsystem_c.dylib`_pthread_wqthread + 404 
    frame #18: 0x00007fff9971c1d1 libsystem_c.dylib`start_wqthread + 13 

Типичный день на сервере

  • Сервер перестал падать

  • Память перестала исчезать в никуда
  • Производительность:
    меньше в ~3 раза
  • Простота поддержки:
    БЕСЦЕННО

2600

178

127

134

345

DISCLAIMER

Response time

Пульс GC

getFakeHistory = function (size) {
    //generate 20kb of data
};

function Record (id, description) {
    this.id = id;
    this.description = description;
}

Record.prototype.doMagic = function () {
    return this.id + 1;
}

var sum = 0;
for (var i = 0; i < 10000; i++) {
    console.time('create');
    var record = new Record(i, getFakeHistory(20 * 102400));
    sum += record.doMagic();
    console.timeEnd('create');
}
console.log('//' + sum);
                            

New
Old
Large Object

NEW

  • от 1 до 8Mb
  • быстрое выделение памяти

OLD

  • Хранится страницами по 1Mb
  • mark-sweep, mark-compact
var sum = 0;
var records = [];
for (var i = 0; i < 10000; i++) {
    records[i] = new Record();
}

for (var i = 0; i < 10000; i++) {
    console.time('create');
    var record = records[i].set(1, getFakeHistory(20 * 102400));
    sum += records[i].doMagic();
    console.timeEnd('create');
}
console.log('//' + sum);

До

После

[большие массивы]

var MAX_PLAYER_ID = 10000;
var data = new Array(MAX_PLAYER_ID);

var sum = 0;
console.time('access');
for (var i = 0; i < 10000000; i++) {
    var pos = ~~(Math.random() * MAX_PLAYER_ID);
    if (typeof data[pos] === 'undefined') {
        data[pos] = 1;
    } else {
        data[pos]++;
    }
}

console.timeEnd('access');
data.reduce(function (sum, e) { return sum + parseInt(e, 0); }, 0);

100    / 111ms
1000  / 115ms
10000 / 123ms

99999

137ms

100000

258ms

--allow-natives-syntax

function hasFastElements(obj) {
  return %HasFastSmiElements(obj) ||
      %HasFastSmiOrObjectElements(obj) ||
      %HasFastObjectElements(obj) ||
      %HasFastDoubleElements(obj) ||
      %HasFastHoleyElements(obj);
}

Массивы

  • Fast Elements / Dictionary Elements
  • delete может переключить режим
  • Преаллокация массива быстрее
  • Кроме больших массивов >100k элементов
  • Обращение к неинициализированным элементам - 2х затраты по производительности
var MAX_PLAYER_ID = 100000;
var data = [];
for (var i = 0; i < MAX_PLAYER_ID; i++) {
    data.push(0);
}

100000

 

258ms

 

137ms

По следам Кнута

function Player (id) {
  this.name = 'SomeName';
  //...
}

Player.prototype.setPremium(premiumInfo) {
  this.premium = true;
  this.premiumInfo = premiumInfo
}

var player = new Player(123);
players.push(player);
//...
if (PremiumChecker.isPremium(player)) {
  player.setPremium(PremiumChecker.getPremiumInfo(player));
  setTimeout(removePlayerFn(player), 3600); // delete players[i]
}

v8: что внутри

Скрытые классы

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Full

  • Мгновенно запускает код
  • Не делает предположений о типах данных
  • Использует "Inline Cache" для улучшения производительности на лету

Optimizing

  • Вызывается только для "горячих" функций
  • Выводит типы из "Inline cache"
  • Пытается выполнить inline всего что только можно

Два компилятора

А как же asm.js?

call = $$$

Никаких call

this.premium = true;

Boxing

edsds

Легкие способы выстрелить себе в ногу

  • Объекты разных типов в массиве (возможно даже скрытых)
  • Нарушение мономорфности
  • delete players[i]
  • try / catch - не оптимизируется никогда
  • Удивить engine

"Not optimized: Optimized too many times"

Найти и обезвредить

No DOM
No libraries
No ideas

--trace_opts --trace_deopts

[marking 0x31bfb004bc29 <JS Function random (SharedFunctionInfo 0x31bfb00398b1)>
for recompilation, reason: small function, ICs with typeinfo: 31/31 (100%), generic ICs: 0/31 (0%)]
 took 0.043, 0.180, 0.054 ms]

[marking 0x244913034d19 <JS Function (SharedFunctionInfo 0x244913023ca9)> for recompilation
reason: hot and stable, ICs with typeinfo: 16/25 (64%), generic ICs: 0/25 (0%)]

Hot & Stable - мечта любого JS-engine

То же и в Chrome

"Not optimized: ForInStatement is not fast case"

for (var prop in obj) {
  /* много-много-кода */
}
function f(p) {
  /* много кода */
}
for (var prop in obj) {
  f(prop);
}

Не оптимизируется :(

  • Генераторы
  • for-of
  • try-catch
  • try-finally
  • let и сompound assignment ( let a = 3; a += 2; )
  • proto, get, set

FDConf #15 Медленный JS

By Illya Klymov

FDConf #15 Медленный JS

Слайды с моего доклада на http://fdconf.dy

  • 2,779