## Coverage Report

#### Created: 2026-05-04 15:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)

    /home/liu/actions-runner/_work/ccv/ccv/test/unit/nnc/parallel.tests.c

    Line
    Count
    Source
    1
    #include "case.h"
    2
    #include "ccv_case.h"
    3
    #include "ccv_nnc_case.h"
    4
    #include <ccv.h>
    5
    #include <nnc/ccv_nnc.h>
    6
    #include <nnc/ccv_nnc_easy.h>
    7
    8
    TEST_SETUP()
    9
    {
    10
      ccv_nnc_init();
    11
    }
    12
    13
    TEST_CASE("tensor symbol copy map grows dynamically on set copy")
    14
    1
    {
    15
    1
      ccv_nnc_symbolic_graph_t* const symbolic_graph = ccv_nnc_symbolic_graph_new();
    16
    1
      const ccv_nnc_tensor_symbol_t a = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "a");
    17
    1
      REQUIRE_EQ(ccv_nnc_tensor_symbol_copy(symbolic_graph, a, 1).d, CCV_NNC_NO_TENSOR_SYMBOL, "copy lookup should be empty before set_copy");
    18
    1
      const ccv_nnc_tensor_symbol_t a_copy = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "a_copy");
    19
    1
      ccv_nnc_tensor_symbol_set_copy(symbolic_graph, a, 1, a_copy);
    20
    1
      REQUIRE_EQ(ccv_nnc_tensor_symbol_copy(symbolic_graph, a, 1).d, a_copy.d, "set_copy should allocate the copy map when missing");
    21
    1
      const ccv_nnc_tensor_symbol_t b = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "b");
    22
    1
      REQUIRE_EQ(ccv_nnc_tensor_symbol_copy(symbolic_graph, b, 1).d, CCV_NNC_NO_TENSOR_SYMBOL, "copy lookup should tolerate symbols outside the current map");
    23
    1
      const ccv_nnc_tensor_symbol_t b_copy = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "b_copy");
    24
    1
      ccv_nnc_tensor_symbol_set_copy(symbolic_graph, b, 1, b_copy);
    25
    1
      REQUIRE_EQ(ccv_nnc_tensor_symbol_copy(symbolic_graph, b, 1).d, b_copy.d, "set_copy should grow rows for later canonical symbols");
    26
    1
      const ccv_nnc_tensor_symbol_t c = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "c");
    27
    1
      const ccv_nnc_tensor_symbol_t c_copy = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "c_copy");
    28
    1
      ccv_nnc_tensor_symbol_set_copy(symbolic_graph, c, 2, c_copy);
    29
    1
      REQUIRE_EQ(ccv_nnc_tensor_symbol_copy(symbolic_graph, c, 2).d, c_copy.d, "set_copy should grow rank count when no exec copy map exists");
    30
    1
      REQUIRE_EQ(ccv_nnc_tensor_symbol_copy(symbolic_graph, a, 1).d, a_copy.d, "rank growth should preserve existing copy entries");
    31
    1
      REQUIRE_EQ(ccv_nnc_tensor_symbol_copy(symbolic_graph, a, 2).d, CCV_NNC_NO_TENSOR_SYMBOL, "new rank entries should default to no copy");
    32
    1
      ccv_nnc_tensor_symbol_set_copy(symbolic_graph, a, 1, a_copy);
    33
    1
      REQUIRE_EQ(ccv_nnc_tensor_symbol_copy(symbolic_graph, c, 2).d, c_copy.d, "setting a smaller rank should not shrink existing rank entries");
    34
    1
      ccv_nnc_tensor_symbol_set_copy(symbolic_graph, b, 1, NO_TENSOR_SYMBOL);
    35
    1
      REQUIRE_EQ(ccv_nnc_tensor_symbol_copy(symbolic_graph, b, 1).d, CCV_NNC_NO_TENSOR_SYMBOL, "set_copy should clear an existing copy entry");
    36
    1
      ccv_nnc_symbolic_graph_free(symbolic_graph);
    37
    1
    }
    38
    39
    TEST_CASE("schedule a simple graph for parallel execution")
    40
    1
    {
    41
    1
      ccv_nnc_symbolic_graph_t* const symbolic_graph = ccv_nnc_symbolic_graph_new();
    42
    1
      const ccv_nnc_tensor_symbol_t x = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "x");
    43
    1
      const ccv_nnc_tensor_symbol_t y = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "y");
    44
    1
      const ccv_nnc_tensor_symbol_t z = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "z");
    45
    1
      ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWPROD_FORWARD(), TENSOR_SYMBOL_LIST(x, y), TENSOR_SYMBOL_LIST(z), "mul");
    46
    1
      const ccv_nnc_tensor_symbol_t a = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "a");
    47
    1
      const ccv_nnc_tensor_symbol_t b = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "b");
    48
    1
      const ccv_nnc_tensor_symbol_t c = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "c");
    49
    1
      ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWSUM_FORWARD(), TENSOR_SYMBOL_LIST(a, b), TENSOR_SYMBOL_LIST(c), "sum");
    50
    1
      const ccv_nnc_tensor_symbol_t d = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "d");
    51
    1
      ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWDIV_FORWARD(), TENSOR_SYMBOL_LIST(z, c), TENSOR_SYMBOL_LIST(d), "div");
    52
    1
      const ccv_nnc_tensor_symbol_t d0 = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "d0");
    53
    1
      ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWLOG_FORWARD(), TENSOR_SYMBOL_LIST(d), TENSOR_SYMBOL_LIST(d0), "log");
    54
    1
      const ccv_nnc_tensor_symbol_t d1 = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "d1");
    55
    1
      ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWEXP_FORWARD(), TENSOR_SYMBOL_LIST(d), TENSOR_SYMBOL_LIST(d1), "exp");
    56
    1
      const ccv_nnc_tensor_symbol_t d2 = ccv_nnc_tensor_symbol_new(symbolic_graph, CPU_TENSOR_NHWC(32F, 1), "d2");
    57
    1
      ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_EWSUM_FORWARD(), TENSOR_SYMBOL_LIST(d0, d1), TENSOR_SYMBOL_LIST(d2), "sum1");
    58
    1
      ccv_nnc_graph_exec_symbol_autogen(symbolic_graph, 0, 0, CCV_NNC_AUTOGEN_ALL_EXECS | CCV_NNC_AUTOGEN_SOURCES_AND_DESTINATIONS);
    59
    1
      SYMBOLIC_GRAPH_GEN(symbolic_graph, CCV_NNC_LONG_DOT_GRAPH);
    60
    1
      ccv_nnc_graph_t* graph;
    61
    1
      ccv_nnc_tensor_arena_t* tensor_arena;
    62
    1
      ccv_nnc_graph_exec_arena_t* graph_exec_arena;
    63
    1
      ccv_nnc_symbolic_graph_compile(symbolic_graph, ccv_nnc_default_compile_params,
    64
    1
        0, 0,
    65
    1
        TENSOR_SYMBOL_LIST(d2),
    66
    1
        SYMBOLIC_GRAPH_SOURCES(symbolic_graph), SYMBOLIC_GRAPH_DESTINATIONS(symbolic_graph),
    67
    1
        &graph, &tensor_arena, &graph_exec_arena);
    68
    1
      ccv_nnc_graph_set_default_static_schedule(graph, CCV_STREAM_CONTEXT_CPU, 0);
    69
    1
      GRAPH_GEN(graph, CCV_NNC_LONG_DOT_GRAPH);
    70
    1
      ccv_nnc_tensor_t* const x_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, x);
    71
    1
      x_tensor->data.f32[0] = 2;
    72
    1
      ccv_nnc_tensor_t* const y_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, y);
    73
    1
      y_tensor->data.f32[0] = 0.21;
    74
    1
      ccv_nnc_tensor_t* const a_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, a);
    75
    1
      a_tensor->data.f32[0] = 2.2;
    76
    1
      ccv_nnc_tensor_t* const b_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, b);
    77
    1
      b_tensor->data.f32[0] = 3.2;
    78
    1
      ccv_nnc_graph_run(graph, 0, TRAVERSE_FULL, 0, 0);
    79
    1
      ccv_nnc_tensor_t* const d2_tensor = ccv_nnc_tensor_from_symbol(tensor_arena, d2);
    80
    1
      const float dv = 2 * 0.21 / (2.2 + 3.2);
    81
    1
      REQUIRE_EQ_WITH_TOLERANCE(d2_tensor->data.f32[0], logf(dv) + expf(dv), 1e-5, "result should be equal");
    82
    1
      ccv_nnc_symbolic_graph_free(symbolic_graph);
    83
    1
      ccv_nnc_graph_free(graph);
    84
    1
      ccv_nnc_tensor_arena_free(tensor_arena);
    85
    1
      ccv_nnc_graph_exec_arena_free(graph_exec_arena);
    86
    1
    }
    87
    88
    TEST_CASE("schedule symbolic graph to data parallel")
    89
    1
    {
    90
    1
      ccv_nnc_symbolic_graph_t* const symbolic_graph = ccv_nnc_symbolic_graph_new();
    91
    1
      const ccv_nnc_tensor_symbol_t x = ccv_nnc_tensor_symbol_new(symbolic_graph, GPU_TENSOR_NHWC(000, 32F, 16, 32, 32, 3), 0);
    92
    1
      const ccv_nnc_tensor_symbol_t w1 = ccv_nnc_tensor_symbol_new(symbolic_graph, GPU_TENSOR_NHWC(000, 32F, 8, 5, 5, 3), 0);
    93
    1
      const ccv_nnc_tensor_symbol_t bias1 = ccv_nnc_tensor_symbol_new(symbolic_graph, GPU_TENSOR_NHWC(000, 32F, 8), 0);
    94
    1
      const ccv_nnc_tensor_symbol_t y1 = ccv_nnc_tensor_symbol_new(symbolic_graph, GPU_TENSOR_NHWC(000, 32F, 16, 32, 32, 8), 0);
    95
    1
      const ccv_nnc_graph_exec_symbol_t conv1 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_CONVOLUTION_FORWARD(1, 8, 5, 5, 3), TENSOR_SYMBOL_LIST(x, w1, bias1), TENSOR_SYMBOL_LIST(y1), "conv1");
    96
    1
      ccv_nnc_graph_exec_symbol_set_hint(symbolic_graph, conv1, HINT((1, 1), (2, 2)));
    97
    1
      const ccv_nnc_tensor_symbol_t y2 = ccv_nnc_tensor_symbol_new(symbolic_graph, GPU_TENSOR_NHWC(000, 32F, 16, 16, 16, 8), 0);
    98
    1
      const ccv_nnc_graph_exec_symbol_t avg2 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_AVERAGE_POOL_FORWARD(2, 2), TENSOR_SYMBOL_LIST(y1), TENSOR_SYMBOL_LIST(y2), "avg2");
    99
    1
      ccv_nnc_graph_exec_symbol_set_hint(symbolic_graph, avg2, HINT((2, 2)));
    100
    1
      const ccv_nnc_tensor_symbol_t w3 = ccv_nnc_tensor_symbol_new(symbolic_graph, GPU_TENSOR_NHWC(000, 32F, 8, 5, 5, 8), 0);
    101
    1
      const ccv_nnc_tensor_symbol_t bias3 = ccv_nnc_tensor_symbol_new(symbolic_graph, GPU_TENSOR_NHWC(000, 32F, 8), 0);
    102
    1
      const ccv_nnc_tensor_symbol_t y3 = ccv_nnc_tensor_symbol_new(symbolic_graph, GPU_TENSOR_NHWC(000, 32F, 16, 8, 8, 8), 0);
    103
    1
      const ccv_nnc_graph_exec_symbol_t conv3 = ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_CONVOLUTION_FORWARD(1, 8, 5, 5, 3), TENSOR_SYMBOL_LIST(y2, w3, bias3), TENSOR_SYMBOL_LIST(y3), "conv3");
    104
    1
      ccv_nnc_graph_exec_symbol_set_hint(symbolic_graph, conv3, HINT((2, 2), (2, 2)));
    105
    1
      const ccv_nnc_tensor_symbol_t y4 = ccv_nnc_tensor_symbol_new(symbolic_graph, GPU_TENSOR_NHWC(000, 32F, 16, 1, 1, 8), 0);
    106
    1
      ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_AVERAGE_POOL_FORWARD(8, 8), TENSOR_SYMBOL_LIST(y3), TENSOR_SYMBOL_LIST(y4), "avg4");
    107
    1
      const ccv_nnc_tensor_symbol_t label = ccv_nnc_tensor_symbol_new(symbolic_graph, GPU_TENSOR_NHWC(000, 32F, 16), "label");
    108
    1
      const ccv_nnc_tensor_symbol_t y5 = ccv_nnc_tensor_symbol_new(symbolic_graph, GPU_TENSOR_NHWC(000, 32F, 16, 1, 1, 8), "y5");
    109
    1
      const ccv_nnc_tensor_symbol_t loss = ccv_nnc_tensor_symbol_new(symbolic_graph, ccv_nnc_tensor_auto, "loss");
    110
    1
      ccv_nnc_graph_exec_symbol_new(symbolic_graph, CMD_SOFTMAX_CROSSENTROPY_FORWARD(), TENSOR_SYMBOL_LIST(y4, label), TENSOR_SYMBOL_LIST(loss, y5), "softmax crossentropy");
    111
    1
      ccv_nnc_graph_exec_symbol_autogen(symbolic_graph, 0, 0, CCV_NNC_AUTOGEN_ALL_EXECS | CCV_NNC_AUTOGEN_SOURCES_AND_DESTINATIONS);
    112
    1
      ccv_nnc_tensor_symbol_t updated_params[4];
    113
    1
      ccv_nnc_tensor_symbol_t gradients[4];
    114
    1
      const int saved_aux_size = ccv_nnc_minimizer_saved_aux_size(CMD_SGD_FORWARD(0, 0.001, 1, 0.99, 0.9, 0.9));
    115
    1
      ccv_nnc_tensor_symbol_map_t saved_aux[saved_aux_size * 4];
    116
    1
      ccv_nnc_graph_exec_symbol_t updated_execs[4];
    117
    1
      ccv_nnc_symbolic_graph_minimize(symbolic_graph, CMD_SGD_FORWARD(0, 0.001, 1, 0.99, 0.9, 0.9), TENSOR_SYMBOL_LIST(loss), TENSOR_SYMBOL_LIST(w1, bias1, w3, bias3), 0, 0, SYMBOLIC_GRAPH_SOURCES(symbolic_graph), SYMBOLIC_GRAPH_DESTINATIONS(symbolic_graph), gradients, updated_params, saved_aux, updated_execs);
    118
    1
      ccv_nnc_symbolic_graph_data_parallel(symbolic_graph, 2, TENSOR_SYMBOL_LIST(w1, bias1, w3, bias3), 0, 0, 0, gradients, 4, 0, CCV_NNC_PARALLEL_REDUCE_OP_SUM, SYMBOLIC_GRAPH_SOURCES(symbolic_graph), updated_execs, 4);
    119
    1
      SYMBOLIC_GRAPH_GEN(symbolic_graph, CCV_NNC_LONG_DOT_GRAPH);
    120
    1
      ccv_nnc_symbolic_graph_free(symbolic_graph);
    121
    1
    }
    122
    123
    #include "case_main.h"