     std::cout << "3-node-1-edge      (g33)  : " << counts.g33 << std::endl;
     std::cout << "3-node-independent (g34)  : " << counts.g34 << std::endl;
 
+    std::cout << std::endl;
+    std::cout << "    Timing (us)" << std::endl;
+    std::cout << "====================" << std::endl;
+    std::cout << "total elapsed      : " << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << std::endl;
+    std::cout << "cudaMalloc         : " << std::chrono::duration_cast<std::chrono::microseconds>(malloc_end - begin).count() << std::endl;
+    std::cout << "cudaMemcpy (input) : " << std::chrono::duration_cast<std::chrono::microseconds>(memcpy_input_end - malloc_end).count() << std::endl;
+    std::cout << "kernel (graphlets) : " << std::chrono::duration_cast<std::chrono::microseconds>(kernel_end - kernel_begin).count() << std::endl;
+    std::cout << "cudaMemcpy (output): " << std::chrono::duration_cast<std::chrono::microseconds>(memcpy_output_end - kernel_end).count() << std::endl;
+    std::cout << "cudaFree           : " << std::chrono::duration_cast<std::chrono::microseconds>(free_end - memcpy_output_end).count() << std::endl;
+    std::cout << "aggregate          : " << std::chrono::duration_cast<std::chrono::microseconds>(end - memcpy_output_end).count() << std::endl;
+
     return EXIT_SUCCESS;
 }

    cudaMalloc((void**)&E_v_ptr, E_size * 2);
    cudaMalloc((void**)&outputs_ptr, outputs_size);

    // Copy data structures from main memory to allocated GPU memory
    cudaMemcpy(V_ptr, V.data(), V_size, cudaMemcpyHostToDevice);
    cudaMemcpy(E_ptr, E.data(), E_size, cudaMemcpyHostToDevice);

#include <iostream>
#include <string>
#include <vector>
#include <chrono>

// Struct containing edge outputs computed in parallel
// One produced per thread, aggregated after GPU computation
