TypechoJoeTheme

MetMan's Blog

网站页面

ODC库编程实践

MetMan博 主神仙
2024-12-02
/
0 评论
/
67 阅读
/
678 个字
/
百度已收录
12/02
本文最后更新于 2024年12月02日,已超过 89天没有更新。如果文章内容或图片资源失效,请留言反馈,我会及时处理,谢谢!

前面提到过odb-2数据格式看成是二维表格型数据,但表格中每列的数据类型可以不同。开发人员可能考虑到通用性以及读写性能,在实现时将表格数据统一处理成双精度的二维数组(也可以看成更一般的一维数组)。

real(8), target :: data(ncols, nrows)

rc = encoder%set_data(data, column_major)

此时需要解决两个问题:

  1. 二维数组在内存中的布局,即以列主元还是行主元的内存布局。
  2. 如果column的类型不是双精度,那么就存在数据类型转换,对于数值类型还算简单,但字符类型如何处理?

第一个问题,我们从ODC API看起。

C API odc_encoder_set_data_array函数用于设置编码的输入数组。函数第二个参数是指定的输入数组,这里使用const void*类型符,通用类型指针。第三个参数是指数组(表格)的宽度,以字节为单位。第四个参数是数组的行数。最后一个参数是指列主元布局每列宽度,以字节为单位,一般为8字节,如果是行主元,传入0值。

对应的Fortran API,则是类对象encoder的成员函数set_data,查看接口实现,可以看出实际上调用的是encoder_set_data_array函数,进而调用C API odc_encoder_set_data_array。其实现如下所示:

    function encoder_set_data_array(encoder, data, column_major) result(err)
        class(odc_encoder), intent(inout) :: encoder
        real(dp), intent(in), target :: data(:,:)
        logical, intent(in), optional :: column_major
        integer(c_long) :: width, height
        integer(c_int) :: columnMajorWidth
        integer :: err
        logical(c_bool) :: l_column_major = .true.

        if (present(column_major)) l_column_major = column_major
        if (l_column_major) then
            width = size(data, 2) * double_size
            height = size(data, 1)
            columnMajorWidth = 8
        else
            width = size(data, 1) * double_size
            height = size(data, 2)
            columnMajorWidth = 0
        end if
        err = odc_encoder_set_data_array(encoder%impl, c_loc(data), width, height, columnMajorWidth)
    end function

Fortran API接口简化很多,根据参数column_major确定表格数组的高度、宽度。

  • column_major = .false.,即行主元局部:一行内每列数据在内存中连续。对于Fortran语言,低维变化最快。所以一般声明数组为data(ncols,nrows)。表格宽度为ncols*double_size字节,高度为nrows
  • column_major = .true.,即列主元布局:一列内每行数据在内存中连续。对于Fortran语言一般声明data(nrows,ncols),其中第1维元素在内存中连续,代表了row连续,其高度为nrows,而宽度是ncols*double_size字节。

那么对于Fortran程序调用ODC库处理odb-2数据,哪种内存布局更好?

我认为行主元内存布局更佳。前面介绍过对于超过8字节宽度的字符变量会导致字符串在内存中占据多列。在列主元内存布局下如果一列放不下所有字符串,跨越多个列,但列与列之间在内存中不连续,访存效果很变差。此外,处理观测数据一般是对一条一条观测记录(行)处理的,保证一条记录(行)内内存连续可能会更好。

第二个问题的解决方法是通过Fortran内置函数transfer进行二进制位级的拷贝。

比如对于长度为5的字符类型转换为双精度浮点型

character(5) :: expver_str 

! loop for nrows:
  write(expver_str, '(a,a)') 'xxxx', '' // c_null_char 
  data(1, i) = transfer(expver_str, data(1, i))  ! expver 

因为要传递给C函数,字符类型变量要添加终止符c_null_char

对于跨越多列的字符串,

character(16) :: wigos_str
! loop for nrows:
write(wigos_str, '(a,i0.2,a)') '0-12345-0-678', i - 1, '' // c_null_char
data(4:5, i) = transfer(wigos_str, data(4:5, i))  ! wigos@hdr
fortranodcodb
朗读
赞(0)
赞赏
感谢您的支持,我会继续努力哒!
版权属于:

MetMan's Blog

本文链接:

https://blog.metman.top/index.php/archives/160/(转载时请注明本文出处及文章链接)

评论 (0)

互动读者

标签云

最新回复

  1. tqymnonccc打酱油
    2024-09-27
  2. toibdpojay打酱油
    2024-09-22
  3. yvctxyevvw打酱油
    2024-09-22
  4. frezhwzwuq打酱油
    2024-09-22
登录
X
用户名
密码