Сравнение времени выполнения вывода массива в макросе и плагине Р7-Офис
В статье «Эффективность заполнения диапазона массивом» был проведен анализ увеличения скорости вывода массива целиком в сравнении с выводом через цикл в макросах Р7-Офиса. Теперь проведем сравнение скорости выполнения такой же задачи в плагине Р7-Офиса по сравнению с макросом.

Возможно, наш плагин пригодится вам в изучении механизма написания своих программ-плагинов в Р7-Офис.

Мы оставили часть кода в разделе «баловство» из соображений возможной пользы для новичков. Плагин закраски активной ячейки уже приводился в нашем базовом курсе по плагинам Р7-Офис, но реализованный там функционал был символическим.
HTML-код дан в Приложении 5, чтобы не перегружать основной текст. CSS и стили используются встроенные в HTML. Там же в Приложении 4 и файл config.json. Чтобы изменить картинку плагина на свою, в файле config.json замените resources/light/stp100.png на нужный вам файл.

Остальные дополнительные папки и файлы (например, папка translations) можете взять из любого плагина. Скорее всего все заработает и без них.

Рабочая директория должна выглядеть так:
Для формирования плагина выделите все файлы в этой директории и упакуйте (ZIP) в файл плагина, измените расширение на .plugin и подключите к Р7-Офис через меню Плагины – Настройка.

Если остались вопросы общего характера, посмотрите наш курс по плагинам Р7-Офис, но, скорее всего, у вас и так все заработает.

Код плагина на JavaScript в трех файлах: code.js – основной файл (Приложение 1), scripts/ fun_btn.js - файл с функциями-обработчиками кнопок (Приложение 2) и scripts/ tests.js - файл с тестами заполнения данными из массива (Приложение 3).

Интерфейс плагина:
Нижний раздел – «баловство» предназначен для закраски активной ячейки выбранным цветом. Для сбора статистики по скорости заполнения листа данными из случайного массива раздел не нужен.

Для сбора статистики предназначен верхний раздел. Горизонтальное заполнение массивом на 10 000 и на 100 000 случайных элементов, ниже вертикальное заполнение массивом на 10 000 и на 100 000 случайных элементов.

Для работы плагина нужно, чтобы в текущей книге имелось два листа: inp и rez. Их назначение описано ниже.

При вызове соответствующего пункта плагина на листе inp очищаются верхние строки и левые столбцы (в расчете времени выполнения теста эти операции не участвуют), формируется массив со случайными данными (также не участвует в тесте). После этого на лист inp выводятся элементы массива целиком и по циклу в 2 соответствующие строки для горизонтального вывода или в соответствующие 2 столбца для вертикального вывода, с замером соответствующих двух интервалов времени.

На лист rez выводятся параметры (количество элементов массива) и результаты теста (время в миллисекундах). Дополнительно выводится расчетное отношение времени выполнения по циклу и всем массивом и среднее значение отношения (формула в F1). На листе rez желательно, но необязательно сделать заголовки и в F1 ввести формулу =СРЗНАЧ(E:E).
При повторном тесте на листе rez автоматически добавляется строка с новыми результатами теста.

Тестирование рекомендуется проводить сериями. Типы серий по соответствующим кнопкам: Горизонтально 10000, Горизонтально 100000, Вертикально 10000, Вертикально 100000.

Перед началом серии нужно очистить данные на листе rez (заголовки пригодятся, удалять не обязательно) и нажать кнопку Очистить счетчик.

Количество тестов в серии рекомендуется не менее 10. В серии не стоит смешивать типы тестов.
Тесты:
Было проведено четыре серии тестов: Горизонтально 10000, Горизонтально 100000, Вертикально 10000, Вертикально 100000. Результаты в сериях приведены ниже.

Добавлены данные по аналогичным тестам в макросах.
Горизонтально 10 000:
Горизонтально 100 000:
Вертикально 10 000:
Вертикально 100 000:
Выводы по результатам тестов
1.Время выполнения тестов для плагина Р7-Офис в среднем почти в два раза лучше для вывода массива с помощью группового оператора, чем в случае использования поэлементного вывода в цикле.

2.Время выполнения тестов для плагина Р7-Офис до 1.5 раз лучше (быстрее) тестов с использованием макросов Р7-Офис.

3.Соотношение Цикл/Массив для плагина Р7-Офис очень близко к такому же соотношению для макроса Р7-Офис

Приложение 1
Основной файл code.js:
(function(window, undefined){
  //Функция API вызываемая редактором при инициализации плагина пользователем
  window.Asc.plugin.init = function(){
      //Средствами DOM назначим функцию changeColor на обработку нажатия на кнопку
      document.getElementById('btn-1').addEventListener('click', change_color);    
      document.getElementById('raschet_h_10').addEventListener('click', raschet_h_10);    
      document.getElementById('raschet_h_100').addEventListener('click', raschet_h_100);    
      document.getElementById('raschet_v_10').addEventListener('click', raschet_v_10);    
      document.getElementById('raschet_v_100').addEventListener('click', raschet_v_100);    
      document.getElementById('raschet_clear').addEventListener('click', raschet_clear);    
  };
  
  window.Asc.plugin.button = function(id){
      this.executeCommand("close", "");
  };

})(window, undefined);
Приложение 2
Файл с функциями-обработчиками кнопок fun_btn.js:
var num_out=0;

function raschet_h_10(){
  console.log("function raschet_h_10()");
  test_arr_h(10000,num_out);
  num_out++;
}

function raschet_h_100(){
  console.log("function raschet_h_100()");
  test_arr_h(100000,num_out);
  num_out++;
}

function raschet_v_10(){
  console.log("function raschet_v_10()");
  test_arr_v(10000,num_out);
  num_out++;
}

function raschet_v_100(){
  console.log("function raschet_v_100()");
  test_arr_v(100000,num_out);
  num_out++;
}

function raschet_clear(){
  console.log("function raschet_clear() num_out="+num_out);
  num_out=0;
  console.log("num_out="+num_out);
}

  //Сменить цвет ячейки, которую перед вызовом функции выделил пользователь
function change_color(){
  let sel=document.getElementById('combo-1').value;
  console.log("Цвет:"+sel);
  let cbox1=document.getElementById('cbox1').checked;  //true/false
  console.log(cbox1);
  
      //Создание "песочницы" js, в поторой происходит работа с документом
  Asc.scope.sel=sel;    //Параметры в callCommand
  Asc.scope.cbox1=cbox1;
  if(!cbox1) {
    document.getElementById('txtxarea-1').value=sel+" Но запрещена закраска";
  }
  else {
    document.getElementById('txtxarea-1').value=sel;
  }

  window.Asc.plugin.callCommand(function() {
    console.log("CC sel:"+Asc.scope.sel);
    console.log(Asc.scope.cbox1);
    if (Asc.scope.cbox1) {
      let oWorksheet =Api.GetActiveSheet();//Получить активный лист   
      let newColor  = Api.CreateColorFromRGB(64, 64, 64)    
      switch(Asc.scope.sel){
      case "Синий":
            newColor=Api.CreateColorFromRGB(0, 0, 127);//Создать синиий
      break;
      case "Красный":
            newColor=Api.CreateColorFromRGB(127, 0, 0);//Создать красный цвет
      break;
      case "Зеленый":
            newColor=Api.CreateColorFromRGB(0, 127, 0);//Создать зеленый цвет
      break;
      }   
      if(oWorksheet!=undefined){
        let oActiveCell = oWorksheet.GetActiveCell();//Получить активную ячейку
        if(oActiveCell!=undefined){//Залить активную ячейку, если она существует
          oActiveCell.SetFillColor(newColor);
        }
      }
    }
  }, undefined,true); //true нужно чтобы редактор перерисовал лист! window.Asc.plugin.callCommand(function()
}; //   function changeColor()
Приложение 3
Файл с тестами заполнения данными из массивов tests.js:
function test_arr_h(l_buf4, num_out) {  // проверка горизонтального диапазона
  //10 000 Среднее массив-цикл  158,9 256,4 1,63
  //100 000 Среднее 1,46  2,68  1,85
  console.log("function test_arr_h(l_buf4)"+l_buf4+" num_out="+num_out);
  Asc.scope.l_buf4=l_buf4;
  Asc.scope.num_out=num_out;
  
  window.Asc.plugin.callCommand(function() {
    let l=Asc.scope.l_buf4;
    let n_out=Asc.scope.num_out;
    console.log("length:"+l);
    let ws =Api.GetSheet("inp");//Получить лист inp 
    let ws_rez =Api.GetSheet("rez");//Получить лист rez
    ws.GetRange("1:20").Clear();  
    ws.GetRange("A:M").Clear();
    if(ws!=undefined){
      let oActiveCell = ws.GetActiveCell();//Получить активную ячейку
      if(oActiveCell!=undefined){
        var buffer = new ArrayBuffer(l*4);
        var Uint32 = new Uint32Array(buffer);
        var arr_h = [];
        for(let i = 0; i < l; ++i) {
          Uint32[i] = ((Math.random() * 0x100) | 0)+1000*i;
          arr_h[i] = Uint32[i];
        }
        let l_uint=Uint32.length;
        let l_arr_h=arr_h.length;
        console.log("buffer:"+buffer.byteLength);
        console.log("Uint32:"+l_uint);
        console.log("Length arr_horiz:"+l_arr_h);
        let rng_h=ws.GetRange(ws.GetRangeByNumber(2,5),ws.GetRangeByNumber(2,5+l_arr_h-1));
        console.time('horiz_arr');
        let tm_1=performance.now();
        rng_h.SetValue(arr_h);
        tm_1=performance.now()-tm_1;
        console.timeEnd('horiz_arr');  //10000 353ms
        console.time('horiz_arr_n');
        let tm_2=performance.now();
        for (let i=0;i<l_uint;i++){ ws.GetRangeByNumber(0,i+5).SetValue(arr_h[i]);}
        console.timeEnd('horiz_arr_n');  //10000 470ms
        tm_2=performance.now()-tm_2;
        tm_1=Math.round(tm_1*100)/100
        tm_2=Math.round(tm_2*100)/100;
        console.log("mytime1="+tm_1);
        console.log("mytime2="+tm_2);
        ws.GetRangeByNumber(0,0).SetValue(tm_1);
        ws.GetRangeByNumber(1,0).SetValue(n_out);
        ws.GetRangeByNumber(2,0).SetValue(tm_2);
        ws_rez.GetRangeByNumber(n_out+1,0).SetValue(n_out+1);
        ws_rez.GetRangeByNumber(n_out+1,1).SetValue(l);
        ws_rez.GetRangeByNumber(n_out+1,2).SetValue(tm_1);
        ws_rez.GetRangeByNumber(n_out+1,3).SetValue(tm_2);
        ws_rez.GetRangeByNumber(n_out+1,4).SetValue(Math.round(tm_2/tm_1*100)/100);
      }
    }
  }, undefined,true); //true нужно чтобы редактор перерисовал лист! window.Asc.plugin.callCommand(function()
}

function test_arr_v(l_buf4, num_out) {  // проверка вертикального диапазона
  // 10 000 Среднее массив-цикл 193,1 352,6 1,89
  // 100 000 Среднее  1,70  3,53  2,08
  console.log("function test_arr_v(l_buf4)"+l_buf4+" num_out="+num_out);
  Asc.scope.l_buf4=l_buf4;
  Asc.scope.num_out=num_out;
  window.Asc.plugin.callCommand(function() {
    console.log("window.Asc.plugin.callCommand(function()");
    let l=Asc.scope.l_buf4;
    let n_out=Asc.scope.num_out;
    console.log("length:"+l);
    let ws =Api.GetSheet("inp");//Получить лист inp 
    let ws_rez =Api.GetSheet("rez");//Получить лист rez
    ws.GetRange("1:20").Clear();  
    ws.GetRange("A:M").Clear();
    if(ws!=undefined){
      let oActiveCell = ws.GetActiveCell();//Получить активную ячейку
      if(oActiveCell!=undefined){
        var buffer = new ArrayBuffer(l*4);
        var Uint32 = new Uint32Array(buffer);
        var arr_v = [];
        for(var i = 0; i < l; ++i) {
             Uint32[i] = ((Math.random() * 0x100) | 0)+1000*i;
             arr_v[i] = [Uint32[i]];
        }
        let l_uint=Uint32.length;
        let l_arr_v=arr_v.length;
        console.log("buffer:"+buffer.byteLength);
        console.log("Uint32:"+l_uint);
        console.log("arr_vert:"+l_arr_v);
        let rng_v=ws.GetRange(ws.GetRangeByNumber(1,1),ws.GetRangeByNumber(1+l_arr_v-1,1));

        let tm_1=performance.now();
        console.time('vert_arr');
        rng_v.SetValue(arr_v);
        console.timeEnd('vert_arr');
        tm_1=performance.now()-tm_1;
        
        let tm_2=performance.now();
        console.time('vert_arr_n');
        for (let i=0;i<l_uint;i++){ ws.GetRangeByNumber(i+1,3).SetValue(arr_v[i]);}
        console.timeEnd('vert_arr_n');
        tm_2=performance.now()-tm_2;

        tm_1=Math.round(tm_1*100)/100
        tm_2=Math.round(tm_2*100)/100;
        console.log("mytime1="+tm_1);
        console.log("mytime2="+tm_2);
        ws.GetRangeByNumber(0,0).SetValue(tm_1);
        ws.GetRangeByNumber(1,0).SetValue(n_out);
        ws.GetRangeByNumber(2,0).SetValue(tm_2);
        ws_rez.GetRangeByNumber(n_out+1,0).SetValue(n_out+1);
        ws_rez.GetRangeByNumber(n_out+1,1).SetValue(l);
        ws_rez.GetRangeByNumber(n_out+1,2).SetValue(tm_1);
        ws_rez.GetRangeByNumber(n_out+1,3).SetValue(tm_2);
        ws_rez.GetRangeByNumber(n_out+1,4).SetValue(Math.round(tm_2/tm_1*100)/100);
      }
    }
  }, undefined,true); //true нужно чтобы редактор перерисовал лист! window.Asc.plugin.callCommand(function()
}
Приложение 4
Файл config.json
{
    "name" : "Массивы в плагине",
    "guid" : "asc.{aeac6ddc-e622-4fd1-b47b-b61eba31b7e9}",
    "version": "0.0.2",

    "variations" : [
        {
            "description" : "Плагин массивов",
            "url"         : "index.html",

             "icons": [ 
                "resources/light/stp100.png ",
                "resources/light/stp100.png " 
            ],
              "icons2": [
                {
          "100%": {
            "normal": "resources/light/stp100.png"
          },          
          "style": "light"
        },
        {
          "100%": {
            "normal": "resources/dark/stp100.png"
          },
          "style": "dark"
        }
      ],    
           "isViewer"        : true,
            "EditorsSupport"  : ["cell"],

            "isVisual"        : true,
            "isModal"         : false,
            "isInsideMode"    : true,

            "initDataType"    : "",
            "initData"        : "",

            "isUpdateOleOnResize" : false,

            "buttons"         : []           
        }
    ]
}
Приложение 5.
Файл index.html
<!DOCTYPE html>
<!--stp test-->
<html lang="ru">

  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> 
    <style type="text/css">
      html, body {
        margin: 0px;
        padding: 0px;
        /* overflow: hidden; */
        width:100%;
        height:100%;
      }
      .header {
        font-size: 14px;
        font-weight: bold;
      }

      .mybtn{
        font-size: 16px;
        font-weight: bold;
      }

      .lbl_header{
        width:100%;
        color: rgb(30, 168, 134);
        font-size: 12px;
        font-weight: bold;
      }      
    </style>

        <script type="text/javascript" src="https://onlyoffice.github.io/sdkjs-plugins/v1/plugins.js"></script>
        <script type="text/javascript" src="https://onlyoffice.github.io/sdkjs-plugins/v1/plugins-ui.js"></script>
        <link rel="stylesheet" href="https://onlyoffice.github.io/sdkjs-plugins/v1/plugins.css">  
    
  <script type="text/javascript" src="code.js"></script>
  <script type="text/javascript" src="scripts/fun_btn.js"></script>
  <script type="text/javascript" src="scripts/tests.js"></script>
</head>

<body>
  <label class="h_ver">stp_array 0.0.5</label>
  <table style="width: 100%;">
    <td style="width:50%;">                 
      <label class="header">Интерфейс расчета</label>
     </td>
     <tr>
        <td colspan="3">
         <button class ="mybtn" id="raschet_h_10" style="width:100%;">Горизонтально 10000</button>                    
        </td>
      </tr>

    <tr>
        <td colspan   ="3">
        <button class ="mybtn" id="raschet_h_100" style="width:100%;">Горизонтально 100000</button>                    
      </td>
    </tr>
    <tr>
      <td colspan="3">
       <button class ="mybtn" id="raschet_v_10" style="width:100%;">Вертикально 10000</button>                    
      </td>
    </tr>
    <tr>
      <td colspan="3">
       <button class ="mybtn" id="raschet_v_100" style="width:100%;">Вертикально 100000</button>                    
      </td>
    </tr>
    <tr>
      <td colspan="3">
       <button class ="mybtn" id="raschet_clear" style="width:100%;">Очистить счетчик</button>                    
      </td>
    </tr>
    <tr>
      <td colspan="3">
       <textarea id="txt_rez" class ="mybtn" style="width:100%;height: 100%;">Книга с листами inp и rez !!!</textarea> 
      </td>
     </tr>                       

     <tr>
      <td style="width:50%;">                 
      <label class="header">_</label>
     </td>
    </tr>                       

    <td style="width:50%;">                 
      <label class="header">Интерфейс закраски (баловство)</label>
     </td>
      <tr>
      <td colspan="3">
       <button id="btn-1" class ="mybtn" style="width:100%;">Закрасить</button>                    
      </td>
    </tr>
    <tr>
   <td style="width:50%;">                 
    <label class="header">Цвет</label>
   </td>
   <td colspan="2">
     <select id="combo-1" style="width:100%;">
       <option>Синий</option>
       <option>Красный</option>
       <option>Зеленый</option>
     </select>
   </td>
  </tr>
  <tr>
   <td colspan="2">                    
      <label class="header">Разрешить закраску</label>
   </td>
   <td style="width:25%;">
     <input type="checkbox" id="cbox1" checked></input>
   </td>
  </tr>
  <tr>
   <td colspan="3">
    <textarea id="txtxarea-1" style="width:100%;height: 100%;">Состояние</textarea> 
   </td>
  </tr>                       
 </table>
</body>
</html>
Поддержка слушателей курса
"Основы Java Script для Р7"