C&C++   发布时间:2022-04-03  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了c – 为什么在解析二进制数据时memcpy比reinterpret_cast慢?大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
TLDR:我忘了启用编译器优化.启用优化后,性能(几乎)相同.

原帖

从二进制数据读取整数时,我注意到memcpy比铸造解决方案慢.

版本1:reinterpret_cast,由于潜在的对齐问题而臭,但也更快(?)

int get_int_v1(const char * data) { return *reinterpret_cast<const int*>(data); }

版本2:memcpy,正确且稍慢:

int get_int_v2(const char * data) { int result; memcpy(&result,data,sizeof(result)); return result; }

我有a benchmark on Ideone.

供将来参,代码是:

#include <cstdlib>
#include <cstdio>
#include <cString>
#include <ctime>
#include <iostream>
#include <vector>
#include <sys/time.h>

double get_current_time()
{
    timeval tv;
    gettimeofday(&tv,null);
    return double (tv.tv_seC) + 0.000001 * tv.tv_usec;
}

int get_int_v1(const char * data) { return *reinterpret_cast<const int*>(data); }
int get_int_v2(const char * data) { int result; memcpy(&result,sizeof(result)); return result; }

const unsigned iterations = 200 * 1000 * 1000;

double test_v1(const char * c,unsigned & prevent_optimization)
{
    double start = get_current_time();
    for (unsigned i = 0; i != iterations; ++i)
    {
        prevent_optimization += get_int_v1(c);
    }
    return get_current_time() - start;
}

double test_v2(const char * c,unsigned & prevent_optimization)
{
    double start = get_current_time();
    for (unsigned i = 0; i != iterations; ++i)
    {
        prevent_optimization += get_int_v2(c);
    }
    return get_current_time() - start;
}

int main()
{
    srand(time(0));

    // Initialize data
    std::vector<int> numbers(1000);
    for (std::vector<int>::size_type i = 0; i != numbers.size(); ++i)
    {
        numbers[i] = i;
    }

    // Repeat benchmark 4 times.
    for (unsigned i = 0; i != 4; ++i)
    {
        unsigned p = 0;
        std::vector<int>::size_type index = rand() % numbers.size();
        const char * c = reinterpret_cast<const char *>(&numbers[index]);    
        std::cout << "v1: " << test_v1(c,p) << std::endl;
        std::cout << "v2: " << test_v2(c,p) << std::endl << std::endl;
    }
}

结果是:

v1: 0.176457
v2: 0.557588

v1: 0.17654
v2: 0.220581

v1: 0.176826
v2: 0.22012

v1: 0.176131
v2: 0.220633

我的问题是:

>我的基准是否正确?
>如果是,那么为什么v2(使用memcpy)会变慢?由于两个版本都返回了数据的副本,我认为应该没有性能差异.
>如何实施正确,快速解决方案?

更新

我很傻,忘了虑Ideone不执行编译器优化.我还稍微调整了一下代码并提出了以下内容

#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cString>
#include <ctime>
#include <iomanip> 
#include <iostream> 
#include <vector>
#include <sys/time.h>

double get_current_time()
{
    timeval tv;
    gettimeofday(&tv,null);
    return double (tv.tv_seC) + 0.000001 * tv.tv_usec;
}

struct test_cast
{
    int operator()(const char * data) const 
    {
        return *((int*)data);
    }
};

struct test_R_223_11845@emcpy
{
    int operator()(const char * data) const 
    {
        int result;
        memcpy(&result,sizeof(result));
        return result;
    }
};

struct test_std_copy
{
    int operator()(const char * data) const 
    {
        int result;
        std::copy(data,data + sizeof(int),reinterpret_cast<char *>(&result));
        return result;
    }
};

enum
{
    iterations = 2000,container_size = 2000
};

std::vector<int> get_random_numbers()
{
    std::vector<int> numbers(container_sizE);
    for (std::vector<int>::size_type i = 0; i != numbers.size(); ++i)
    {
        numbers[i] = rand();
    }
    return numbers;
}

std::vector<int> get_random_inDices()
{
    std::vector<int> numbers(container_sizE);
    for (std::vector<int>::size_type i = 0; i != numbers.size(); ++i)
    {
        numbers[i] = i;
    }
    std::random_shuffle(numbers.begin(),numbers.end());
    return numbers;
}

template<typename Function>
unsigned benchmark(const Function & f,unsigned & counter)
{
    std::vector<int> container = get_random_numbers();
    std::vector<int> inDices = get_random_inDices();
    double start = get_current_time();
    for (unsigned iter = 0; iter != iterations; ++iter)
    {
        for (unsigned i = 0; i != container.size(); ++i)
        {
            counter += f(reinterpret_cast<const char*>(&container[inDices[i]]));
        }
    }
    return unsigned(0.5 + 1000.0 * (get_current_time() - start));
}

int main()
{
    srand(time(0));
    unsigned counter = 0;

    std::cout << "cast:      " << benchmark(test_cast(),counter) << " ms" << std::endl;
    std::cout << "memcpy:    " << benchmark(test_R_223_11845@emcpy(),counter) << " ms" << std::endl;
    std::cout << "std::copy: " << benchmark(test_std_copy(),counter) << " ms" << std::endl;
    std::cout << "(counter:  " << counter << ")" << std::endl << std::endl;

}

结果现在几乎相等(std :: copy除外,因为某些原因速度较慢):

g++ -o test -O0 -Wall -Werror -Wextra -pedantic-errors main.cpp
cast:      56 ms
memcpy:    60 ms
std::copy: 290 ms
(counter:  2854155632)

g++ -o test -O1 -Wall -Werror -Wextra -pedantic-errors main.cpp
cast:      9 ms
memcpy:    14 ms
std::copy: 20 ms
(counter:  3524665968)

g++ -o test -O2 -Wall -Werror -Wextra -pedantic-errors main.cpp
cast:      4 ms
memcpy:    5 ms
std::copy: 20 ms
(counter:  2590914608)

g++ -o test -O3 -Wall -Werror -Wextra -pedantic-errors main.cpp
cast:      4 ms
memcpy:    5 ms
std::copy: 18 ms
(counter:  2590914608)

解决方法

您需要查看发出的代码.显然,优化器“应该”能够将memcpy转换为单个可能未对齐的int大小读入返回值,但是如果你看到不同的时间,那么我认为x86意味着它没有.

在我的机器上,使用gcc和-O2我总是得到0.09.使用-O3我总是得到0(我没有检查它是否比时间粒度更快,或者优化器已经删除了所有代码).

如此可能,答案只是你没有使用正确的编译器标志(或者ideone没有).

在可能未对齐的读取需要来自对齐读取的不同指令的体系结构中,reinterpret_cast可以发出对齐的读取,而memcpy可能必须发出未对齐的读取(取决于函数调用方式 – 在这种情况下,数据是事实上已经对齐,但我不知道编译器在什么条件下可以证明这一点.在那种情况下,我希望reinterpret_cast代码可以比memcpy更快,但当然在有人传入未对齐指针的情况下它将是不正确的.

大佬总结

以上是大佬教程为你收集整理的c – 为什么在解析二进制数据时memcpy比reinterpret_cast慢?全部内容,希望文章能够帮你解决c – 为什么在解析二进制数据时memcpy比reinterpret_cast慢?所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。