Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   помогите соптимизировать алгоритм для V8 (https://javascript.ru/forum/misc/49366-pomogite-soptimizirovat-algoritm-dlya-v8.html)

newobject 10.08.2014 14:01

помогите соптимизировать алгоритм для V8
 
Проблема наблюдается только на V8. На слабом железе при быстром вводе наблюдается зависание инпута. Буквы не печатаются, а потом выскакивают разом по нескольку штук. Также, когда несколько раз жмешь бекспейс. Мое предположение, изначально, было в том, что при вводе следующего символа продолжает выполняться цикл внутри функции search, отсюда и тормоза. Я добавил что-то типа прерывания, однако ситуация не изменилась. Помогите, пожалуйста.
search=function(pattern){
   var out=[]
   var re=new RegExp(pattern, "i")
   var current_flag=window.flag
   for(var i=0; i<base.length; i++){
      if(current_flag!==window.flag) return;//вот тут прерывание
      if(base[i].match(re)) {
      base[i]=base[i].replace(/((\d{4} \d{2}-\d{3})|(\d{4} \d{2}-\d{3} \d{1}-\d{2}-\d{2}))$/, "<b>$1</b>")
      out.push(base[i])
   }
 }
return out
}

firstSearch=function(){
   d.innerHTML=""
   input.style.color=null
   var out=search(input.value)
   if(out.length<1) return secondSearch()
   var str=out.join("<br><br>")
   d.innerHTML=str
}

secondSearch=function(){
   input.style.color="red"
   d.innerHTML="Нет результатов для <b>"+input.value+"</b>"
}
onload=firstSearch
input.oninput=firstSearch
input.focus()
onkeydown=function(){input.focus(); window.flag=new Date().getTime()}


UPD:
d -- это див на странице
input -- input на странице
base -- массив ~900 строк

UPD2:
Вот возможная причина:
Насколько я себе это представляю, js однопоточен, и когда я ввожу следующий символ в input, он не может сразу отреагировать, не дождавшись завершения предыдущего поиска. Чтобы прервать этот цикл, внутри search, я устанавливаю флаг на событие нажатия клавиши, если следующяя клавиша была нажата, условие выполняется и мы выходим из цикла на текущей итерации. Это было мое предположение, но не помогло. Похоже я лоханулся в том, что событие нажатия само по себе не может наступить, не дождавшись выполнения цикла, поэтому пока цикл не выполнится, прерывание лфаг не изменится, вот в чем ошибка наверное. Но как это фиксить -- хз

ixth 10.08.2014 14:20

Тут не нужна оптимизация под V8. Тут нужен жирный рефакторинг. Кроме того, ты приводишь только часть кода. Где объявлены d, input, base?

newobject 10.08.2014 14:24

Цитата:

Сообщение от ixth
Кроме того, ты приводишь только часть кода. Где объявлены d, input, base?

d -- это див на странице
input -- input на странице
base -- массив ~900 строк

А при чем тут рефакторинг?

ixth 10.08.2014 14:34

При том, что самая лучшая оптимизация — смена подхода. Подожди полчасика, я запощу свой вариант, может он не будет тормозить.

ixth 10.08.2014 14:40

Зачем, кстати, используются  current_flag/window.flag?

newobject 10.08.2014 14:48

Цитата:

Сообщение от ixth
Зачем, кстати, используются current_flag/window.flag?

Цитата:

Мое предположение, изначально, было в том, что при вводе следующего символа продолжает выполняться цикл внутри функции search, отсюда и тормоза. Я добавил что-то типа прерывания, однако ситуация не изменилась.
Насколько я себе это представляю, js однопоточен, и когда я ввожу следующий символ в input, он не может сразу отреагировать, не дождавшись завершения предыдущего поиска. Чтобы прервать этот цикл, внутри search, я устанавливаю флаг на событие нажатия клавиши, если следующяя клавиша была нажата, условие выполняется и мы выходим из цикла на текущей итерации. Это было мое предположение, но не помогло. Похоже я лоханулся в том, что событие нажатия само по себе не может наступить, не дождавшись выполнения цикла, поэтому пока цикл не выполнится, прерывание лфаг не изменится, вот в чем ошибка наверное. Но как это фиксить -- хз

ixth 10.08.2014 14:56

В этом примере все продолжает тормозить? http://jsfiddle.net/ainop/g41x6tto/ Скинь куда-нибудь пример строк, которые ты используешь.

MallSerg 10.08.2014 15:03

Имхо: клинический случай ((

Оставлю тут на всякий случай http://hitech.vesti.ru/news/view/id/5351

newobject 10.08.2014 15:15

ixth,
Да, тормозит еще сильней
пример строки
' Пупкина Светлана Николаевна товаровед (по многооборотной таре и обеспечению ж/д транспортом) 26 Отдел сбыта и транспортной логистики 5977 88-9595 5-40-20'

ixth 10.08.2014 15:34

Цитата:

Сообщение от newobject (Сообщение 325163)
ixth,
Да, тормозит еще сильней
пример строки
' Пупкина Светлана Николаевна товаровед (по многооборотной таре и обеспечению ж/д транспортом) 26 Отдел сбыта и транспортной логистики 5977 88-9595 5-40-20'

А, допустим, такой вариант? http://jsfiddle.net/ainop/g41x6tto/1/

newobject 10.08.2014 15:56

ixth,
Так поменьше, но все равно тормозит. Думаю, на моих строках сильней оригинала будет тормозить.
У меня есть тут одна мысля. Надо как то сделать, чтобы поиск начиналсмя с таймаутом, а в этом таймауте проверялось не изменился ли за это время инпут. Думаю, надо в этом направлении копать. Потом отпишусь. Пока не делай ничо, а то мне жалко твоего времени:), если не получится, тогда будем продолжать:)

ixth 10.08.2014 16:22

Ты не поверишь… Мой вариант почти так и делает. Он ждет, когда пользователь закончит ввод и только тогда запускает поиск. Плюс, тебе же не критично, что поиск ведется регуляркой? Можно заменить его на indexOf: http://jsfiddle.net/ainop/g41x6tto/4/

Больше никаких оптимизаций сделать, наверное, нельзя. Я бы перенес все это на сторону сервера и делал запросы через ajax.

newobject 10.08.2014 17:26

ixth,
Я изменил search вот так
search=function(the_value){
   var test=function(){return input.value==the_value}
   var out=[]
   var re=new RegExp(the_value, "i")
   for(var i=0; i<base.length; i++){
      if(!test()) break;
      if(base[i].match(re)) {
      base[i]=base[i].replace(/((\d{4} \d{2}-\d{3})|(\d{4} \d{2}-\d{3} \d{1}-\d{2}-\d{2}))$/, "<b>$1</b>")
      out.push(base[i])
   }
 }
return out
}

И один хрен тормозит. Во всех остальных браузерах летает. Может это просто V8 кривой? Или я фигню написал?

MallSerg 10.08.2014 17:32

А если данных будет в 100 раз больше?
не возникает ощущение что задачу решаете не совсем правильно?

newobject 10.08.2014 17:38

MallSerg,
Я бы с удовольствием выслушал ваши предложения.

MallSerg 10.08.2014 18:51

Задача не озвучена полностью тут только весьма странная часть решения неизвестной задачи ((.
Возможно стоит задача поиска совпадений в текстовых строках.

Со стороны задача выглядит как забивание саморезов в бетонную стену с помощью булки хлеба. ))

newobject 10.08.2014 18:59

MallSerg,
Задача - банальный поиск выборки по массиву с последующим выводом результата.

MallSerg 11.08.2014 01:38

Цитата:

Сообщение от newobject
банальный поиск выборки

Как эти слова вообще могли оказаться рядом?
как вообще могло прийти в голову выводить результат из 900 строк на каждое нажатие клавиши?

На ладно давай допустим что есть люди читающие по 500 - 600 строк в секунду зачем использовать регулярные выражения для простого поиска чем не устраивает valueOF ?
Почему функция называется поиск а на самом деле она генерит HTML ?

Цитата:

Сообщение от newobject
остальных браузерах летает

остальные браузеры разумно игнорируют код постоянно перестраивающий дом перерисовывающий страницу с такой частотой пользователь его все равно не сможет увидеть

newobject 11.08.2014 01:55

MallSerg,
Вы господин, какую то феерическую ахинею несете. Или вникни в код наконец, или расслабься и отдохни.

ixth 11.08.2014 09:46

Цитата:

Сообщение от MallSerg (Сообщение 325268)
остальные браузеры разумно игнорируют код постоянно перестраивающий дом перерисовывающий страницу с такой частотой пользователь его все равно не сможет увидеть

Все интересно и весело. См. продолжение срача тут: http://javascript.ru/forum/misc/4937...uet-opera.html

newobject 11.08.2014 10:52

ixth,
он чушь несет несусветную, там перерисовка идет только один раз, после цикла, все браузеры все перерисовывают одинаково, иначе как юзер смог бы увидеть результаты поиска? Глупо было бы.

ixth 11.08.2014 10:57

Да, но "после цикла" раньше вызывалось при каждом нажатии на клавишу. Думаю, он про это.

newobject 11.08.2014 11:00

Да, может и про это, и это тоже чушь. Браузер не может заранее знать, в какой момент будет нажата следующая клавиша, и будет ли нажата. Может быть там установлены таймауты минммальные, но это не принципиально. Визуально не видно никаких задержек, все выводится моментально.

newobject 11.08.2014 11:07

ixth,
Проблема там не в выводе результатов, а в том, что во время выполнения цикла пользовательский ввод не может попасть в инпут, он ждет окончания цикла.

newobject 11.08.2014 11:43

ixth,
Да, кстати, я там заметил, что ты в своем коде используешь map для массива, типа, декларативное рулит, да, но это еще дополнительные тормоза+утечки памяти, возможно. Не надо так делать. Я вот тут протестил немножко
test=function(f, i){
	console.time(f.name)
		while(i--){f()}
	console.timeEnd(f.name)
}


mymap=function(arr, f){
   var newArr=[]
   for(var i=0; i<arr.length; i++){
      newArr[i]=f(arr[i])
   }
   return newArr
}

mymappush=function(arr, f){
   var newArr=[]
   for(var i=0; i<arr.length; i++){
       newArr.push(f(arr[i]))
   }
   return newArr
}

thismap=function(f){
   var newArr=[]
   for(var i=0; i<this.length; i++){
      newArr[i]=f(this[i])
   }
   return newArr
}


thismappush=function(f){
   var newArr=[]
   for(var i=0; i<this.length; i++){
       newArr.push(f(this[i]))
   }
   return newArr
}




arr=[0,1,2,3,4,5,6,7,8,9]

f=function(x){return x}
arr.thismap=thismap
arr.thismappush=thismappush

function tst_mymap(){
   mymap(arr, f)
}
function tst_mymappush(){
   mymappush(arr, f)
}
function tst_thismap(){
  arr.thismap(f)
}

function tst_thismappush(){
  arr.thismappush(f)
}

function tst_native_map(){
   arr.map(f)
}

i=100000
test_=function(f){return test(f, i)}

test_(tst_mymap)
test_(tst_mymappush)
test_(tst_thismap)
test_(tst_thismappush)
test_(tst_native_map)


//tst_mymap: 77ms
//tst_mymappush: 38ms
//tst_thismap: 75ms
//tst_thismappush: 81ms
//tst_native_map: 301ms


Тестил только на ноде, но результат как бы намекает. Врядли оно хот где-то быстрей будет.
ЗЫ к моему удивлению, в старой опере действительно быстрей

Started: tst_mymap
tst_mymap: 7363ms (7362900µsec)
Started: tst_mymappush
tst_mymappush: 8298ms (8298206µsec)
Started: tst_thismap
tst_thismap: 6963ms (6963449µsec)
Started: tst_thismappush
tst_thismappush: 8201ms (8200867µsec)
Started: tst_native_map
tst_native_map: 5507ms (5506914µsec)

Но в хроме:
tst_mymap: 32.000ms
tst_mymappush: 29.000ms
tst_thismap: 31.000ms
tst_thismappush: 27.000ms
tst_native_map: 460.000ms

В фф
[11:55:18.280] tst_mymap: таймер запущен
[11:55:18.420] tst_mymap: 140мс
[11:55:18.420] tst_mymappush: таймер запущен
[11:55:18.562] tst_mymappush: 142мс
[11:55:18.562] tst_thismap: таймер запущен
[11:55:18.711] tst_thismap: 149мс
[11:55:18.712] tst_thismappush: таймер запущен
[11:55:18.872] tst_thismappush: 160мс
[11:55:18.873] tst_native_map: таймер запущен
[11:55:19.166] tst_native_map: 293мс

MallSerg 11.08.2014 11:44

Проблема в выводе результатов
как следствие этой проблемы - пользовательский ввод ждет окончания вывода результатов.

ixth 11.08.2014 12:09

Каким образом map ведет к утечке памяти? И если придираться к мелочам, то почему ты так странно объявляешь функции?

newobject 11.08.2014 12:44

MallSerg,
Возможно ты прав, извиняюсь что нагрубил, я в шоке. :) Я сгенерил массив в несколько тысяч строк, и поставил таймеры. Цикл выполняется 1-3 миллисекунды, основное время занимает отрисовка. Это если верить отладчикам. А я им не верю. Как такое может быть? Просто, видимо выполнение цикла не учитывается толком. Буду дальше эксперементировать.

newobject 11.08.2014 12:47

Цитата:

Сообщение от ixth
Каким образом map ведет к утечке памяти?

Ну, я насколько помню, "родная" реализация map рекурсивная. Возможно они так и сделали.
>>
Цитата:

Сообщение от ixth
И если придираться к мелочам, то почему ты так странно объявляешь функции?

Просто привычка. А это имеет какое-то значение?

ixth 11.08.2014 13:12

Цитата:

Сообщение от newobject (Сообщение 325315)
Ну, я насколько помню, "родная" реализация map рекурсивная. Возможно они так и сделали.

Родная реализация может измениться в любой момент. Принцип мапа в том, что для всех элементов массива вызывается функция, которая преобразует их значение и помещает в другой массив: никакой рекурсией тут не пахнет. Кроме того, гугл про утечки памяти в связи в map ничего не пишет: https://www.google.com/search?q=memo...s+array+map+js

Но да, он медленнее. Т.к. у тебя критичное к скорости место, можешь заменить на for, ничего не имею против, просто мне было удобнее написать с мапом.

Цитата:

Сообщение от newobject (Сообщение 325315)
Просто привычка. А это имеет какое-то значение?

Это излишне многословно. Ты создаешь переменную и присваиваешь ей function expression вместо того, чтобы сразу написать function declaration. Просто лишние телодвижения. Кроме того, твои переменные хойстятся и до присвоения, условно говоря, имя функции есть, а самой функции нет. Кроме того, у функции, объявленной через function expression нет arguments.callee.name. Должны быть еще какие-то тонкие нюансы, но это надо глубоко в спецификацию лезть, а мне лень. В учебнике рекомендуется использовать function declaration почти во всех случаях:

Цитата:

Function Declaration короче и лучше читается. Дополнительный бонус — такие функции можно вызывать до того, как они объявлены.

Используйте Function Expression только там, где это действительно нужно. Например, для объявления функции в зависимости от условий.
И да, ответь мне в той теме, где я показал результаты профайлинга.


Часовой пояс GMT +3, время: 11:20.