Oracle mechanics

23.03.2014

Таймауты выполнения PL/SQL процедуры, Гауссово распределение и статистика будущих периодов

Filed under: Active Session History,hints,Oracle — Игорь Усольцев @ 21:34
Tags: ,

По жалобе на превышение 20 секундного таймаута при выполнении кастомизированной процедуры XXWHY_NOT_INTF.REQUEST, оценка статистики выполнения соответствующего запроса оказалась не очень информативной:

11.2.0.3.@ SQL> @dba_hist_sqlstat "sql_id = 'arhv5vxgj80uf' and snap_id >= 62450"
 
BEGIN_SNAP_TIME  EXECS SQL_ID        ELA_PER_EXEC CPU_PER_EXEC GETS_PER_EXEC READS_PER_EXEC ROWS_PER_EXEC IOWAITS_PER_EXEC PLSQL_PER_EXEC
--------------- ------ ------------- ------------ ------------ ------------- -------------- ------------- ---------------- --------------
...
21.01 08:30        541 arhv5vxgj80uf      2223235      2195982         20628              2             1            19878          23186
21.01 08:00        564 arhv5vxgj80uf      2188863      2145934         20396              4             1            30133          17214
21.01 07:30        562 arhv5vxgj80uf      2187546      2157540         20390              2             1            18394          18801
21.01 07:00        555 arhv5vxgj80uf      2221630      2189168         20563              2             1            22075          21824
21.01 06:30        570 arhv5vxgj80uf      2158897      2127110         20144              2             1            21746          19995
21.01 06:00        566 arhv5vxgj80uf      2191961      2136901         20037              3             1            42246          37670
...

— хотя понятно, что в среднем большая часть времени выполнения процедуры тратилась на ЦПУ (и/или чтение/обработку блоков буферного кэша)

В поисках потенциального источника таймаутов может пригодиться древо ожиданий вызова как самой процедуры (sql_id=arhv5vxgj80uf), так и генерируемых ею рекурсивных запросов:

SQL> @ash_sql_wait_tree "ash.sql_id = 'arhv5vxgj80uf' or ash.top_level_sql_id = 'arhv5vxgj80uf'" 0
 
BLOCKING_TREE  EVENT                    WAITS_COUNT EXECS_COUNT AVG_WA SQL_ID        SQL_TEXT
-------------- ------------------------ ----------- ----------- ------ ------------- -----------------------------------------------
FOREGROUND     On CPU / runqueue               5615        2464      0 3zqr41cv49r88 UPDATE XXGET_SOME_INSTANCE_CHANGES SET ...
FOREGROUND     utl_file I/O                    1044         772    818 arhv5vxgj80uf begin :1 := XXWHY_NOT_INTF.REQUEST(:2,:3); end;
FOREGROUND     db file sequential read           29          29     23 5fc4r572bgc4z SELECT ...
FOREGROUND     direct path read temp             17          17     12 arhv5vxgj80uf begin :1 := XXWHY_NOT_INTF.REQUEST(:2,:3); end;
FOREGROUND     db file sequential read           13          13     41 42hrmtsn25g38 SELECT MIN(TRANSACTION_ID) FROM ...
FOREGROUND     On CPU / runqueue                 13          13      0 arhv5vxgj80uf begin :1 := XXWHY_NOT_INTF.REQUEST(:2,:3); end;
...

— из чего, например, можно заметить значительную долю ожиданий utl_file I/O, вызванных использованием в процедуре пакета UTL_FILE для логирования/трассировки, что вполне могло быть подвержено таймаутам операций ввода-вывода, особенно учитывая факт использования кластерной файловой системы. Однако после профилактического отключения вызовов UTL_FILE таймауты процедуры arhv5vxgj80uf не исчезли, и в качестве основного блокиратора остался только UPDATE с номером 3zqr41cv49r88 со следующей историей/статистикой:

SQL> @dba_hist_sqlstat "sql_id = '3zqr41cv49r88' and snap_id >= 62450"
 
BEGIN_SNAP_TIME  EXECS SQL_ID              PLAN       COST ELA_PER_EXEC CPU_PER_EXEC GETS_PER_EXEC IOWAITS_PER_EXEC CLWAITS_PER_EXEC
--------------- ------ ------------- ---------- ---------- ------------ ------------ ------------- ---------------- ----------------
21.01 08:30        541 3zqr41cv49r88 2060773130          3      2194859      2184738         20041                0                0
21.01 08:00        554 3zqr41cv49r88 2060773130          3      2187311      2174604         20041                0                0
21.01 07:30        555 3zqr41cv49r88 2060773130          3      2187440      2176062         20041                0                0
21.01 07:00        553 3zqr41cv49r88 2060773130          3      2198753      2185684         20041                0                0
21.01 06:30        556 3zqr41cv49r88 2060773130          3      2181710      2171740         20041                0                0
21.01 06:00        549 3zqr41cv49r88 2060773130          3      2206390      2193528         20041                0                0
...

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

ASH-мониторинг плана выполнения показывает что основное время тратится на операцию INDEX SKIP SCAN:

SQL> @ash_sqlmon 3zqr41cv49r88 2060773130 ""
 
ID PLAN_OPERATION         OBJECT_NAME                     QBLOCK_NAME WAIT_PROFILE
-- ---------------------- ------------------------------- ----------- -------------
 0   UPDATE STATEMENT                                                 ON CPU(1);
 1     UPDATE             XXGET_SOME_INSTANCE_CHANGES     UPD$1       ON CPU(1);
 2       INDEX SKIP SCAN  XXGET_SOME_INSTANCE_CHANGES_N2  UPD$1       ON CPU(5057);

, но при внимательном рассмотрении в том же ASH можно наблюдать отдельные выполнения, у которых на ту же операцию уходит до 8 секунд(!):

SQL> with multirec_sql_exec_id as
  2   (select inst_id, sql_id, sql_exec_id, count(*) as seconds_per_exec
  3      from gv$active_session_history
  4     where sql_id = '3zqr41cv49r88'
  5     group by inst_id, sql_id, sql_exec_id)
  6  select seconds_per_exec,
  7         lpad(' ', 2 * sql_plan_line_id) || sql_plan_operation || ' ' || sql_plan_options as SQL_PLAN_OPERATION,
  8         count(distinct sql_exec_id) as EXECS,
  9         count(*) as ASH_RECORDS
 10    from gv$active_session_history
 11    join multirec_sql_exec_id
 12   using (inst_id, sql_id, sql_exec_id)
 13   group by seconds_per_exec, lpad(' ', 2 * sql_plan_line_id) || sql_plan_operation || ' ' || sql_plan_options
 14   order by 1, 2 desc
 15  /
 
SECONDS_PER_EXEC SQL_PLAN_OPERATION          EXECS ASH_RECORDS
---------------- ------------------------ -------- -----------
               1     INDEX SKIP SCAN             1           1
               2     INDEX SKIP SCAN          1579        3158
               3 UPDATE STATEMENT                1           1
               3   UPDATE                        3           3
               3     INDEX SKIP SCAN          4218       12650
               4     INDEX SKIP SCAN            11          44
               6     INDEX SKIP SCAN             1           6
               8     INDEX SKIP SCAN             1           8 -- here be dragonz

— что уже более чем вероятно может приводить к таймаутам в полном соответствии с нормальным распределением)

Типично запрос выполнялся с использованием близких к sysdate значений связанных переменных, и для выполнения выбирался простой, легковоспроизводимый и неэффективный план за номером 2060773130, отражённый во всей предыдущей статистике:

SQL> select sysdate from dual;

SYSDATE
-------------------
25.02.2014 16:01:24

SQL> UPDATE
  2   XXGET_SOME_INSTANCE_CHANGES t SET PROCESS_FLAG = 'P'
  3   WHERE UPDATE_DATE < to_date('02/25/2014 15:33:36', 'mm/dd/yyyy hh24:mi:ss')
  4     AND (PROCESS_FLAG = 'N' OR UPDATE_DATE >= to_date('02/25/2014 15:33:32', 'mm/dd/yyyy hh24:mi:ss'))
  5  /

0 rows updated.

Elapsed: 00:00:02.65

Plan hash value: 2060773130

---------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation        | Name                           | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT |                                |      1 |        |       |     3 (100)|          |      0 |00:00:02.65 |   24234 |
|   1 |  UPDATE          | XXGET_SOME_INSTANCE_CHANGES    |      1 |        |       |            |          |      0 |00:00:02.65 |   24234 |
|*  2 |   INDEX SKIP SCAN| XXGET_SOME_INSTANCE_CHANGES_N2 |      1 |     18 |   180 |     3   (0)| 00:00:01 |      0 |00:00:02.65 |   24234 |
---------------------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("UPDATE_DATE"<TO_DATE(' 2014-02-25 15:33:36', 'syyyy-mm-dd hh24:mi:ss'))
       filter((("UPDATE_DATE">=TO_DATE(' 2014-02-25 15:33:32', 'syyyy-mm-dd hh24:mi:ss') OR "PROCESS_FLAG"='N') AND
              "UPDATE_DATE"<TO_DATE(' 2014-02-25 15:33:36', 'syyyy-mm-dd hh24:mi:ss')))

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
      24234  consistent gets
...

с использованием INDEX SKIP SCAN по составному индексу:

SQL> select * from dba_ind_columns where index_name = 'XXGET_SOME_INSTANCE_CHANGES_N2';
 
INDEX_NAME                     TABLE_NAME                     COLUMN_NAME   COLUMN_POSITION COLUMN_LENGTH CHAR_LENGTH DESCEND
------------------------------ ------------------------------ ------------- --------------- ------------- ----------- -------
XXGET_SOME_INSTANCE_CHANGES_N2 XXGET_SOME_INSTANCE_CHANGES    PROCESS_FLAG                1             1           1 ASC
XXGET_SOME_INSTANCE_CHANGES_N2 XXGET_SOME_INSTANCE_CHANGES    UPDATE_DATE                 2             7           0 ASC

в то время как тот же запрос для вчерашних дат использовал формально более дорогой, но во много раз более эффективный план с CONCATENATION:

SQL> explain plan for
  2  UPDATE XXGET_SOME_INSTANCE_CHANGES t SET PROCESS_FLAG = 'P'
  3   WHERE UPDATE_DATE < to_date('02/24/2014 15:33:36', 'mm/dd/yyyy hh24:mi:ss')        -- sysdate
  4     AND (PROCESS_FLAG = 'N' OR UPDATE_DATE >= to_date('02/24/2014 15:33:32', 'mm/dd/yyyy hh24:mi:ss'))
  5  /

Explained.

SQL> select * from table(dbms_xplan.display('','','+outline'));

PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------
Plan hash value: 1777608577

-----------------------------------------------------------------------------------------------------
| Id  | Operation          | Name                           | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT   |                                |    19 |   190 |     6   (0)| 00:00:01 |
|   1 |  UPDATE            | XXGET_SOME_INSTANCE_CHANGES    |       |       |            |          |
|   2 |   CONCATENATION    |                                |       |       |            |          |
|*  3 |    INDEX RANGE SCAN| XXGET_SOME_INSTANCE_CHANGES_N2 |     1 |    10 |     3   (0)| 00:00:01 |
|*  4 |    INDEX SKIP SCAN | XXGET_SOME_INSTANCE_CHANGES_N2 |    18 |   180 |     3   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      ...
      USE_CONCAT(@"UPD$1" 8 OR_PREDICATES(2) PREDICATE_REORDERS((4 3) (3 4)))
      ...
      END_OUTLINE_DATA
  */

, который можно легко получить для актуального запроса с помощью подсказки из Outline Data:

SQL> UPDATE /*+ USE_CONCAT*/
  2   XXGET_SOME_INSTANCE_CHANGES t SET PROCESS_FLAG = 'P'
  3   WHERE UPDATE_DATE < to_date('02/25/2014 15:33:36', 'mm/dd/yyyy hh24:mi:ss')
  4     AND (PROCESS_FLAG = 'N' OR UPDATE_DATE >= to_date('02/25/2014 15:33:32', 'mm/dd/yyyy hh24:mi:ss'))
  5  /

0 rows updated.

Elapsed: 00:00:00.01 -- против 2+ секунд

Plan hash value: 1777608577

-----------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name                           | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT   |                                |      1 |        |       |     6 (100)|          |      0 |00:00:00.01 |     420 |
|   1 |  UPDATE            | XXGET_SOME_INSTANCE_CHANGES    |      1 |        |       |            |          |      0 |00:00:00.01 |     420 |
|   2 |   CONCATENATION    |                                |      1 |        |       |            |          |      0 |00:00:00.01 |     420 |
|*  3 |    INDEX RANGE SCAN| XXGET_SOME_INSTANCE_CHANGES_N2 |      1 |      1 |    10 |     3   (0)| 00:00:01 |      0 |00:00:00.01 |     408 |
|*  4 |    INDEX SKIP SCAN | XXGET_SOME_INSTANCE_CHANGES_N2 |      1 |     17 |   170 |     3   (0)| 00:00:01 |      0 |00:00:00.01 |      12 |
-----------------------------------------------------------------------------------------------------------------------------------------------

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        420  consistent gets -- против 24к+ у плана 2060773130
...

Объяснение такой странного несоответствия стоимости и времени выполнения можно найти в 10053 трейсе первоначального плана (без конкатенации):

...
  Using prorated density: 0.000002 of col #2 as selectvity of out-of-range/non-existent value pred
...

— статистика таблиц/индексов собиралась во время ночного окна, и естественно для времён близких к sysdate готовой статистики ещё нет:

SQL> select last_analyzed from dba_tables where table_name = 'XXGET_SOME_INSTANCE_CHANGES';

LAST_ANALYZED
-------------------
25.02.2014 05:55:20

SQL> select column_name,
  2         utl_raw.cast_to_varchar2(low_value) as LOW_VALUE,
  3         utl_raw.cast_to_varchar2(high_value) as HIGH_VALUE,
  4         num_nulls,
  5         last_analyzed
  6    from dba_tab_col_statistics
  7   where table_name = 'XXGET_SOME_INSTANCE_CHANGES'
  8     and column_name = 'PROCESS_FLAG'
  9  /
 
COLUMN_NAME   LOW_VALUE  HIGH_VALUE   NUM_NULLS LAST_ANALYZED
------------- ---------- ----------- ---------- -------------------
PROCESS_FLAG  Y          Y                    0 25.02.2014 05:55:20

— кроме того, согласно той же ночной статистике у поля PROCESS_FLAG имеется одно преобладающее значение Y, в смысле все строки были уже успешно processed

Из трейса оптимизатора того же неэффективного выполнения запроса (без конкатенации) следует, что план с конкатенацией Oracle при компиляции прекрасно видит и просчитывает в рамках преобразования or-expansion, но, основываясь на недостаточной статистике, разумно отвергает ввиду высокой стоимости:

...
or-expansion is worse cost:6.002056
...

Из V$SQL/V$SQL_SHARED_CURSOR можно видеть, что механизмы адаптивного управления для этого запроса не запускаются — BIND_AWARE=N, USE_FEEDBACK_STATS=N:

SQL> @shared_cu 3zqr41cv49r88
 
INST    EXECS LAST_LOAD_TIME USERS_OPENING LAST_ACTIVE_TIME ELA_PER_EXEC PLAN_HASH_VALUE OPTIMIZER_COST CHILD BIND_SENSE BIND_AWARE SHAREABLE  USE_FEEDBACK_STATS OPTIMIZER_STATS  BIND_EQ_FAILURE  REASON1            IS_OBSOLETE
---- -------- -------------- ------------- ---------------- ------------ --------------- -------------- ----- ---------- ---------- ---------- ------------------ ---------------- ---------------- ------------------ -----------
   1     6599 25.02 05:56                1 25.02 17:36           2720948      2060773130              3     0 Y          N          Y          N                  N                N                NLS Settings(0)  | N
   1       64 25.02 11:08                0 25.02 17:17           2754175      2060773130              3     1 Y          N          Y          N                  N                N                NLS Settings(0)  | N
   2       35 25.02 12:29                0 25.02 17:36           2761847      2060773130              3     1 Y          N          Y          N                  N                N                NLS Settings(0)  | N
   2     4386 25.02 05:55                0 25.02 17:33           2734835      2060773130              3     0 Y          N          Y          N                  N                N                NLS Settings(0)  | N

ввиду того, что время выполнения запроса в 2-3 секунды недостаточно для запуска мониторинга, определяемого дефолтным значением в 5 секунд параметра:

SQL> @param_ _sqlmon_threshold
 
NAME               VALUE  IS_DEF   DSC
------------------ ------ -------- --------------------------------------------------------------------
_sqlmon_threshold  5      TRUE     CPU/IO time threshold before a statement is monitored. 0 is disabled

По той же причине короткого времени выполнения не слишком удачным кажется использовать для улучшения плана этого запроса dynamic sampling

Интересно, что для запроса, сдобренного подсказкой UPDATE /*+ USE_CONCAT*/…, судя по трейсу оптимизатор безусловно признаёт преимущества использования конкатенации:)

...
or-expansion is better cost:0.000000
...

Поскольку код проблемной процедуры легко поддавался модификации, после фиксации разумного плана методом добавления подсказки /*+ USE_CONCAT*/ время выполнения (и кол-во выполнений) UPDATE изменились драматически:

SQL> @dba_hist_sqlstat "sql_id in ('3zqr41cv49r88','22kaurfq0w2zy') and snap_id > 64205"
 
INST SNAP_ID BEGIN_SNAP_TIME    EXECS SQL_ID              PLAN COST ELA_PER_EXEC CPU_PER_EXEC GETS_PER_EXEC IOWAITS_PER_EXEC
---- ------- --------------- -------- ------------- ---------- ---- ------------ ------------ ------------- ----------------
   2   64222 27.02 04:30         1553 22kaurfq0w2zy 1777608577    6         1152         1085           377                0
   2   64220 27.02 03:30         1431 22kaurfq0w2zy 1777608577    6         1150         1072           378               10
   2   64219 27.02 03:00         1238 22kaurfq0w2zy 1777608577    6         1128         1070           377                0
   1   64218 27.02 02:30          914 22kaurfq0w2zy 1777608577    6         1013          998           377                0
   1   64208 26.02 21:30           74 3zqr41cv49r88 2060773130    3      2641243      2626020         24290                0
   2   64208 26.02 21:30          232 3zqr41cv49r88 2060773130    3      2689011      2674598         24289                0
   1   64207 26.02 21:00          252 3zqr41cv49r88 2060773130    3      2659890      2643392         24290                0
   2   64207 26.02 21:00          232 3zqr41cv49r88 2060773130    3      2684164      2659251         24291                0
   1   64206 26.02 20:30          181 3zqr41cv49r88 2060773130    3      2696550      2679930         24301                0
   2   64206 26.02 20:30          300 3zqr41cv49r88 2060773130    3      2673187      2657893         24299                0

Аналогичным образом улучшилась ситуация с выполнением всей процедуры arhv5vxgj80uf:

SQL> @dba_hist_sqlstat "sql_id = 'arhv5vxgj80uf' and snap_id > 64205"
 
INST SNAP_ID BEGIN_SNAP_TIME    EXECS SQL_ID              PLAN COST ELA_PER_EXEC CPU_PER_EXEC GETS_PER_EXEC IOWAITS_PER_EXEC PLSQL_PER_EXEC
---- ------- --------------- -------- ------------- ---------- ---- ------------ ------------ ------------- ---------------- --------------
...
   2   64222 27.02 04:30         1560 arhv5vxgj80uf          0    0        12259         6930           629             4656           5354
   1   64221 27.02 04:00          136 arhv5vxgj80uf          0    0       174479       125804          5144            45213          42264
   2   64221 27.02 04:00          285 arhv5vxgj80uf          0    0        20819         8248           652            11075           6063
   1   64220 27.02 03:30          301 arhv5vxgj80uf          0    0        21168         8730           708            11597           6101
   2   64220 27.02 03:30         1441 arhv5vxgj80uf          0    0        11950         7017           632             4218           5708
   1   64219 27.02 03:00          485 arhv5vxgj80uf          0    0        15631         6968           634             7932           6699
   2   64219 27.02 03:00         1238 arhv5vxgj80uf          0    0        13372         6787           629             6026           7735
   1   64218 27.02 02:30          914 arhv5vxgj80uf          0    0        11158         6615           629             3987           5718
   2   64218 27.02 02:30          819 arhv5vxgj80uf          0    0        11835         6978           629             4245           5986
...
   2   64208 26.02 21:30          240 arhv5vxgj80uf          0    0      2615825      2592556         23752             7698           6191
   1   64207 26.02 21:00          264 arhv5vxgj80uf          0    0      2558905      2531805         23509             9907           6336
   2   64207 26.02 21:00          238 arhv5vxgj80uf          0    0      2632741      2600004         23973             7320           5973
   1   64206 26.02 20:30          228 arhv5vxgj80uf          0    0      2206896      2144183         19908            45906           8531
   2   64206 26.02 20:30          341 arhv5vxgj80uf          0    0      2394327      2353721         22093            24249           7892
   1   64205 26.02 20:00          188 arhv5vxgj80uf          0    0      2446229      2412080         22431            17563           6288
   2   64205 26.02 20:00          338 arhv5vxgj80uf          0    0      2485061      2439534         22384            21743           6628

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

6 комментариев »

  1. Игорь, а в скрипте dba_hist_sqlstat значения «…_per_exec» вычисляются как (…_delta/executions_delta)? Я правильно понимаю, что это будет корректно только для многократно выполняющихся небольших запросов (когда можно пренебречь переходом выполнения между снимками)?

    комментарий от Egor Rogov — 13.05.2014 @ 16:19 | Ответить

    • Всё верно, добавил запрос dba_hist_sqlstat.sql
      Хороший аргумент, кстати, против сильного сокращения интервала между AWR-снапшотами

      комментарий от Игорь Усольцев — 13.05.2014 @ 16:55 | Ответить

      • Пытаюсь разобраться в теме, очень интересно… Спасибо за подробно рассмотренный пример! Весьма полезно для понимания того, как применять инструмент на практике.

        Насколько я понял, на time_waited можно полагаться только при большом объеме однородной активности или при небольших (меньше секунды) ожиданиях, а в противном случае правильнее смотреть на count.

        Кстати. В скриптах ASH_WAIT_TREE и ASH_WAIT_TREE_HIST среднее время ожидания вычисляется одинаково (по time_waited). Но в последнем случае надо ведь на 10 умножить?

        комментарий от Egor Rogov — 20.05.2014 @ 18:54 | Ответить

        • Не, глупость написал. Среднее время ожидания умножать на 10 не надо, конечно. Зато надо исключить из выборки нулевые time_waited, потому что при длинных (больше секунды) ожиданиях такие строки все сильно испортят в меньшую сторону. Но это относится к обоим скриптам.
          Нет?

          комментарий от Egor Rogov — 20.05.2014 @ 20:11 | Ответить

          • Егор, есть такой интересный документ ASH Architecture and Advanced Usage от архитекторов/разработчиков Oracle, там они тоже рекомендуют исключать TIME_WAITED = 0, но только для коротких ожиданий, для длинных ожиданий такие записи засчитываются с длительностью 1 секунда.
            Скриптик ASH_WAIT_TREE.SQL я поправил, добавив рекомендованные оценки EST_WAITS и EST_AVG_LATENCY_MS
            Для коротких ожиданий оценочные LATENCY получаются лучше, но не всегда и не для всех ожиданий. Основная идея этих скриптов именно в оценке «дерева» зависимостей/блокировок ожиданий, сравнительная оценка длительностей — попутная тема

            комментарий от Игорь Усольцев — 20.05.2014 @ 23:53 | Ответить


RSS feed for comments on this post. TrackBack URI

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

Блог на WordPress.com.

%d такие блоггеры, как: