cpp dll, cu dll and python

  • see practice_020_dll, practice_021_usedll
  • https://luckygg.tistory.com/278
    • (tip) dll 탐색 프로그램: dependency walker
    • (tip) extern “C” 키워드를 붙이면, 네임 맹글링 Name Mangling을 하지 않는다. C++을 함수 오버로딩이 가능하므로 호출될 때 그 구분을 위한 네임 맹글링을 수행하는데, C로 하면 맹글링 안한다. 명시적 링킹으로 특정 함수 호출이 가능해진다.
    • __declspec(dllexport), declspec(dllimport) : dll 외부로 노출된다. 호출 가능하다는 얘기. export로 정의된 함수를 import로 호출한다. 동일한 *.h파일을 호출 프로젝트와 생성 프로젝트에서 동일하게 사용하기 위함.
  • 클래스를 dll로 만들기 https://luckygg.tistory.com/281

generate dll

  • run visual studio
  • create new project > (template) Dynamic Link Library
    • framework.h, pch.h, pch.cpp는 그대로 사용한다.
    • dllmain.cpp는 삭제한다
    • math_clang.cpp, math_clang.h 생성한다.
  • math_clang.cpp
    • 원래의 코드에서 #include “pch.h” #include “math_clang.h”만 추가되었다.
#include "pch.h" // added, use stdafx.h in Visual Studio 2017 and earlier
#include "math_clang.h" // added
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>


/*
 * Multiplies each element of the source array by a constant value.
 *
 * @param pddst     Destination array to store the result.
 * @param pdsrc     Source array to be multiplied.
 * @param dconst    Constant value to multiply each element.
 * @param pnsz      Integer array containing the dimensions [nx, ny, nz].
 *
 * p represents pointer
 * d represents data
 */

void mul_const(float* pddst, float* pdsrc, float dconst, int* pnsz) {
  int nx = pnsz[0];
  int ny = pnsz[1];
  int nz = pnsz[2];
  int id = 0;

  for (int idz = 0; idz < nz; idz++) {
    for (int idy = 0; idy < ny; idy++) {
      for (int idx = 0; idx < nx; idx++) {
        id = ny * nx * idz + nx * idy + idx;
        pddst[id] = pdsrc[id] * dconst;
      }
    }
  }

  return;
}


/*
 * Adds a constant value to each element of the source array.
 *
 * @param pddst     Destination array to store the result.
 * @param pdsrc     Source array to which the constant is added.
 * @param dconst    Constant value to be added to each element.
 * @param pnsz      Integer array containing the dimensions [nx, ny, nz].
 */
void add_const(float* pddst, float* pdsrc, float dconst, int* pnsz) {
  int nx = pnsz[0];
  int ny = pnsz[1];
  int nz = pnsz[2];
  int id = 0;

  for (int idz = 0; idz < nz; idz++) {
    for (int idy = 0; idy < ny; idy++) {
      for (int idx = 0; idx < nx; idx++) {
        id = ny * nx * idz + nx * idy + idx;
        pddst[id] = pdsrc[id] + dconst;
      }
    }
  }

  return;

}

/*
 * Multiplies two arrays element-wise and stores the result in the destination array.
 *
 * @param pddst     Destination array to store the result.
 * @param pdsrc     Source array to be multiplied with another source array.
 * @param pdsrc2    Second source array for element-wise multiplication.
 * @param pnsz      Integer array containing the dimensions [nx, ny, nz].
 */
void mul_mat(float* pddst, float* pdsrc, float* pdsrc2, int* pnsz) {
  int nx = pnsz[0];
  int ny = pnsz[1];
  int nz = pnsz[2];
  int id = 0;

  for (int idz = 0; idz < nz; idz++) {
    for (int idy = 0; idy < ny; idy++) {
      for (int idx = 0; idx < nx; idx++) {
        id = ny * nx * idz + nx * idy + idx;
        pddst[id] = pdsrc[id] * pdsrc2[id];
      }
    }
  }

  return;

}

/*
 * Adds two arrays element-wise and stores the result in the destination array.
 *
 * @param pddst     Destination array to store the result.
 * @param pdsrc     Source array to which the second source array is added.
 * @param pdsrc2    Second source array for element-wise addition.
 * @param pnsz      Integer array containing the dimensions [nx, ny, nz].
 */
void add_mat(float* pddst, float* pdsrc, float* pdsrc2, int* pnsz) {
  int nx = pnsz[0];
  int ny = pnsz[1];
  int nz = pnsz[2];
  int id = 0;

  for (int idz = 0; idz < nz; idz++) {
    for (int idy = 0; idy < ny; idy++) {
      for (int idx = 0; idx < nx; idx++) {
        id = ny * nx * idz + nx * idy + idx;
        pddst[id] = pdsrc[id] + pdsrc2[id];
      }
    }
  }

  return;

}

// gcc -c -fPIC math_clang.c -o math_clang.o
// gcc -shared math_clang.o -o libmath_clang.so
  • math_clang.h
    • __declspec(dllexport), __declspec(dllimport)가 추가되었다.
#pragma once

#ifdef MATHLIBRARY_EXPORTS
#define MATHLIBRARY_API __declspec(dllexport)
#else
#define MATHLIBRARY_API __declspec(dllimport)
#endif

extern "C" MATHLIBRARY_API void mul_const(float* pddst, float* pdsrc, float dconst, int* pnsz);
extern "C" MATHLIBRARY_API void add_const(float* pddst, float* pdsrc, float dconst, int* pnsz);
extern "C" MATHLIBRARY_API void mul_mat(float* pddst, float* pdsrc, float* pdsrc2, int* pnsz);
extern "C" MATHLIBRARY_API void add_mat(float* pddst, float* pdsrc, float* pdsrc2, int* pnsz);
  • build
    • math_clang.dll, math_clang.pdb, math_clang.exp, math_clang.lib 네 개가 모두 나와야 구성이 잘 된 것.
    • 여기서 math_clang.dll만 있으면 가져다가 쓸 수 있다. python과 동일 경로에 이동한다.
  • python
    • ctypes를 통해서 가져온다
    • 기존의 linux에서 so를 썼던 것과 동일하다. dll 생성해내는 것이 문제였다.
## python, c, cu

import os, copy
import numpy as np
import ctypes
from ctypes import *
import sys
import time

if sys.platform.startswith('win'): 
    print('platform is window')
    platform = 'window'
elif sys.platform.startswith('linux'): 
    print('platform is linux')
    platform = 'linux'
else: 
    print('platform is others')
    platform = 'other platform'

pnsz = np.asarray([300, 1024, 760], dtype = np.int32)
mul = np.random.randn()
add = np.random.randn()
src = np.random.randn(pnsz[0], pnsz[1], pnsz[2]).astype(dtype=np.float32)

## numpy in CPU
pre_time_numpy = time.time()
src_numpy = copy.deepcopy(src)
dst_numpy = src_numpy
dst_numpy = dst_numpy * mul
dst_numpy = dst_numpy + add
dst_numpy = dst_numpy * dst_numpy
dst_numpy = dst_numpy + dst_numpy
after_time_numpy = time.time()
print(f'elapsed time numpy: {after_time_numpy - pre_time_numpy}')
print("Result: ", dst_numpy[:4,0,0])

## Clang in CPU
if platform == 'window':
    clang_file = os.path.join(os.path.dirname(__file__), 'math_clang.dll')
elif platform == 'linux':
    clang_file = os.path.join(os.getcwd(), 'libmath_clang.so')

_math_clang = ctypes.CDLL(clang_file)

__mul_const_clang = _math_clang.mul_const
__add_const_clang = _math_clang.add_const
__mul_mat_clang = _math_clang.mul_mat
__add_mat_clang = _math_clang.add_mat

# init
__mul_const_clang.argtypes = (POINTER(c_float), POINTER(c_float), c_float, POINTER(c_int)) # argument
__mul_const_clang.restypes = c_void_p # return variable
__add_const_clang.argtypes = (POINTER(c_float), POINTER(c_float), c_float, POINTER(c_int)) # argument
__add_const_clang.restypes = c_void_p # return variable
__mul_mat_clang.argtypes = (POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_int)) # argument
__mul_mat_clang.restypes = c_void_p # return variable
__add_mat_clang.argtypes = (POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_int)) # argument
__add_mat_clang.restypes = c_void_p # return variable

c_float_p = lambda x: x.ctypes.data_as(POINTER(c_float))
c_int_p = lambda x: x.ctypes.data_as(POINTER(c_int))

##
pre_time_clang = time.time()
src_clang = copy.deepcopy(src)
dst_clang = src_clang
__mul_const_clang(c_float_p(dst_clang), c_float_p(dst_clang), mul, c_int_p(pnsz))
__add_const_clang(c_float_p(dst_clang), c_float_p(dst_clang), add, c_int_p(pnsz))
__mul_mat_clang(c_float_p(dst_clang), c_float_p(dst_clang), c_float_p(dst_clang), c_int_p(pnsz))
__add_mat_clang(c_float_p(dst_clang), c_float_p(dst_clang), c_float_p(dst_clang), c_int_p(pnsz))

after_time_clang = time.time()
print(f'elapsed time clang: {after_time_clang - pre_time_clang}')
print("Result: ", dst_clang[:4,0,0])


cpp에서 dll 가져다가 사용하기

  • 필요한 파일: math_clang.dll, math_clang.h, math_clang.lib
  • 소스코드와 동일 경로에 있으면 설정하지 않아도 괜찮긴 한데, 보통은 명시적으로 경로를 알려준다.
  • header
    • project > 우클릭 > properties > C/C++ > General > Additional Include Directories > header 파일이 들어 있는 경로 입력. 보통은 include
    • 솔루션 탐색기에 external dependencies에서 볼 수 있다.
    • include, 빌드 시점
  • library
    • project > 우클릭 > properties > Linker > General > Additional Library Directories > library 파일이 들어 있는 경로 입력. 보통은 Lib
    • project > 우클릭 > properties > Linker > Input > Additional Dependencies > math_clang.lib 추가. library 파일 이름. 확장자 .lib까지 꼼꼼히 쓴다.
    • 빌드 시점, 함수를 못 찾아온다.
  • dll
    • (수작업) 실행파일과 같은 경로에 math_clang.dll을 복사해둔다.
    • 실행시점
    • (자동) build 후 action으로 dll을 카피하라고 시킨다.
      • project > 우클릭 > properties > Build event > Post build event > Command line > xcopy /y /d “$(ProjectDir)mydll\math_clang.dll” “$(ProjectDir)”
      • projectDir에 실행파일이 위치함
  • main 함수 작성
// usedll.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include "math_clang.h"
#include <iostream>
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <array>

int main() {
  // Python과 동일한 데이터 및 변수 초기화
  int pnsz[] = { 300, 1024, 760 };
  float mul = static_cast<float>(std::rand()) / RAND_MAX;
  float add = static_cast<float>(std::rand()) / RAND_MAX;
  float* src = new float[pnsz[0] * pnsz[1] * pnsz[2]];

  // 데이터 초기화 (예: 랜덤 값 채우기)
  for (int i = 0; i < pnsz[0] * pnsz[1] * pnsz[2]; i++) {
    src[i] = static_cast<float>(std::rand()) / RAND_MAX;
  }

  // 작업 시작 시간 기록
  clock_t start_time = clock();

  // C++ 함수 호출하여 연산 수행
  float* dst = new float[pnsz[0] * pnsz[1] * pnsz[2]];

  // mul_const 함수 호출
  mul_const(dst, src, mul, pnsz);

  // add_const 함수 호출
  add_const(dst, dst, add, pnsz);

  // mul_mat 함수 호출
  mul_mat(dst, dst, dst, pnsz);

  // add_mat 함수 호출
  add_mat(dst, dst, dst, pnsz);

  // 작업 종료 시간 기록
  clock_t end_time = clock();
  double elapsed_time = static_cast<double>(end_time - start_time) / CLOCKS_PER_SEC;

  // 결과 출력
  std::cout << "elapsed time C++: " << elapsed_time << " seconds" << std::endl;
  std::cout << "Result: " << dst[0] << std::endl;

  // 메모리 해제
  delete[] src;
  delete[] dst;

  return 0;
}