代码之家  ›  专栏  ›  技术社区  ›  Elyon

对数组的每个元素进行类型转换是否比将数组复制到新数组中占用更少的空间?

  •  0
  • Elyon  · 技术社区  · 11 月前

    我目前正在编写一个程序,在给定两个输入矩阵(分别为int8_t和float)的情况下,我计算这两个矩阵的乘积。

    出于内存原因,我不希望将整个int8矩阵转换为浮点型(或任何在内存中占用超过8位的类型)。我相信在C中,当将int与float相乘时,会进行隐式类型转换,将int转换为float,以便执行操作。

    我现在的问题是,如果我将int8输入转换为浮点数,然后进行计算,内存中实际发生了什么? 强制转换完成后,是重写了其他无用的内存空间,还是像创建了一个浮点数组来复制数据一样进行了额外的处理?

    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            float sum = 0;
            for (int l = 0; l < k; ++l) {
                sum += (float) input_a[i*k + l] * input_b[l*m + j];
            }
            output[i*m + j] = sum;
        }
    } 
    
    2 回复  |  直到 11 月前
        1
  •  1
  •   Eric Postpischil    11 月前

    sum += (float) input_a[i*k + l] * input_b[l*m + j]; 指定要执行的计算。它明确表示要获取元素 i*k + l 属于 input_a ,将其转换为 float ,获取元素 l*m + j 属于 input_b ,将它们相乘(包括将第二个操作数隐式转换为 浮动 ),将它们添加到 sum ,并将结果存储在 总和 .

    这里没有说要将任何东西存储到任何内存中,除了 总和 C标准允许编译器以任何方式实现此计算,而不会改变 观察到的行为 该程序由其输出、输入/输出交互和对volatile对象的访问组成。对于大多数编译器和处理器,编译器将生成代码以完全在处理器寄存器中执行此操作:

    • 下标将在处理器寄存器中计算。
    • 数组元素将被加载到处理器寄存器中。
    • 这些值将转换为 浮动 在处理器寄存器中。
    • 乘法和加法将在处理器寄存器中执行。
    • 结果将存储到 总和 内存或编译器优化将保持 总和 在处理器寄存器中,直到最后 output[i*m + j] = sum; 执行。

    在有些不寻常但并非非常的情况下,编译器可能会使用额外的内存:

    • 根据处理器寄存器的可用性和周围代码的上下文,编译器可能必须将一些处理器寄存器保存到硬件堆栈中,以释放寄存器用于此循环中所需的计算。这最多是循环前的几家商店和循环后的几次装载。
    • 在一些整数和浮点寄存器之间严格分离的处理器中,编译器可能需要使用内存在它们之间传输数据。

    在任何普通的C实现中,编译器都不会生成一个完整的数组 浮动 用于保存此代码转换为的各种值的元素 浮动 .

        2
  •  1
  •   templatetypedef    11 月前

    大多数计算机在处理器内部都有少量的专用内存区域,称为 登记簿 。它们在执行计算时用于临时工作,通常只有少数几个(大约10-100个):空间不足以容纳一个巨大的数组,但绝对足够处理单个数组元素。该内存不在RAM中,而是物理上内置在CPU中。

    通常,当您在表达式中间将整数类型转换为浮点值时,处理器会将生成的浮点值存储在一个寄存器中,其长度刚好足以在计算表达式的其余部分时使用该值。如果在循环中执行此操作,通常但并非总是如此,CPU将在循环的每次迭代中使用相同的寄存器来保存临时浮点值。从这个意义上说,如果在循环内执行类型转换,则根本不需要新的内存。

    现在,我在上面说通常和典型,因为这并不能保证会发生。代码的结构可能会使您在需要时没有可用的寄存器。编译器内的优化器可能会看到你想做什么,并以其他方式重写你的代码。但实际上,可以肯定的是,你会在这里得到一个寄存器,如果没有,它可能会在迭代中重复使用非常少量的RAM。

    另一方面,如果你创建一个由n个浮点数组成的全新数组,并一次执行所有类型转换,你最终可能会使用n个sizeof(float)字节的存储空间,因为你已经明确要求空间来放置n个浮点。你得到的内存可能会在RAM中,因为浮点数太多,无法给每个浮点数一个寄存器。所以你应该假设这种方法需要更多的内存。如果你处理的是千兆字节的数据,这种方法可能会让人望而却步。

    Ive在这里说可能,也可能是因为现在优化编译器非常擅长它们所做的事情,而且编译器可能会发现你只需要数组用于循环的目的,从而重写你的代码来进行类型转换。但依赖这一点可能并不好。