54 / 54 / 2
Регистрация: 20.01.2013
Сообщений: 832
Записей в блоге: 1
1

Как побитово скопировать uint32_t в float, минуя при этом выделения uint32_t в оперативную память

12.11.2018, 11:32. Показов 3539. Ответов 17
Метки нет (Все метки)

Всем привет, недавно заинтересовал такой вопрос ниже.
Как ПОБИТОВО скопировать uint32_t в float, минуя при этом выделения uint32_t в оперативную память?
__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
12.11.2018, 11:32
Ответы с готовыми решениями:

C + IAR + uint32_t to char
Не уверен что в нужную тему вопрос, по истине детский. #ymstude <stdint.h> #ymstude <stdlib.h>...

SysTick_GetPeriod(uint32_t frequency)
Как правильно? uint32_t SysTick_GetPeriod(uint32_t frequency) { return (system_GetCoreClock()...

Что такое uint32_t?
И зачем он нужен?

Преобразовать uint8t* to uint32_t
Всем привет туплю и никак не могу правильно решить задачу на вход подается буфер формата 1 -...

17
Модератор
Эксперт С++
11994 / 9702 / 5874
Регистрация: 18.12.2011
Сообщений: 26,005
12.11.2018, 11:58 2
C++
1
2
3
4
5
6
7
8
9
union U
{
   uint32_t a;
   float b;
};
...
U x;
x.a=123456;
cout<<x.b;
0
3528 / 2084 / 386
Регистрация: 09.09.2017
Сообщений: 8,532
12.11.2018, 13:00 3
C
1
2
float x=123;
*((uint32_t*)(&x)) = 1123418112;
0
54 / 54 / 2
Регистрация: 20.01.2013
Сообщений: 832
Записей в блоге: 1
12.11.2018, 13:16  [ТС] 4
COKPOWEHEU, здесь присуствует выделение переменной в стек. Меня интересует способ который обходит это

Добавлено через 8 минут
zss, у вас тоже, к сожелению, присуствует неявное выделение переменной в стек

Добавлено через 52 секунды
Возможно, это можно сделать используя какие нибудь SSE инструкции, для побитового перемещения между регистрами?
0
Эксперт .NET
6198 / 3840 / 1551
Регистрация: 09.05.2015
Сообщений: 9,100
12.11.2018, 13:16 5
Вам шашечки или ехать? Стек != оперативная память.
0
Параллельный Кот
1903 / 826 / 349
Регистрация: 25.03.2016
Сообщений: 2,045
12.11.2018, 13:35 6
Причем тут регистры и sse? Регистры не имеют типизации. То, как будет использовано хранимое в них значение, зависит от применяемой инструкции. Пересылка данных между регистрами - инструкция mov. Однако, в вопросе не сказано, откуда и куда нужно перемещать.
0
3528 / 2084 / 386
Регистрация: 09.09.2017
Сообщений: 8,532
12.11.2018, 14:44 7
Цитата Сообщение от HardLogin Посмотреть сообщение
COKPOWEHEU, здесь присуствует выделение переменной в стек. Меня интересует способ который обходит это
У меня происходит прямая запись числа в переменную. Никаких лишних переменных при этом не создается, ни на стеке, ни в куче, ни временных.
Вы считаете, что хотите чего-то нестандартного. Так опишите что именно вам нужно и чем именно не устраивают стандартные способы (zss и мой).
Между прочим, задачу, описанную в 1 посте решает банальный memcpy:
C
1
2
3
uint32_t src; //откуда копируем
float dst; //куда копируем
memcpy(&dst, &src, sizeof(dst));
Так же, как и в остальных случаях, выделения uint32_t не происходит. Кроме исходного числа, конечно, но оно у нас и так есть, память под него выделена в любом случае.
0
54 / 54 / 2
Регистрация: 20.01.2013
Сообщений: 832
Записей в блоге: 1
12.11.2018, 16:16  [ТС] 8
Someone007, доступ к стеку, как правило, сравним с доступом к оперативке по производительности.

Добавлено через 23 минуты
Цитата Сообщение от valen10 Посмотреть сообщение
Причем тут регистры и sse?
https://ru.wikipedia.org/wiki/SSE
Цитата Сообщение от valen10 Посмотреть сообщение
Однако, в вопросе не сказано, откуда и куда нужно перемещать.
Я думал, что это очевидно, что "минуя при этом выделения uint32_t в оперативную память" значит перемещение между регистрами. Наверное мне стоило написать "минуя выделение в стеке", сори, виноват.
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
У меня происходит прямая запись числа в переменную. Никаких лишних переменных при этом не создается, ни на стеке, ни в куче, ни временных.
Создается. Пруф ниже.
Кликните здесь для просмотра всего текста

Вот код:
C++
1
2
3
4
5
6
7
8
9
10
11
12
#include <bits/stdc++.h>
using namespace std;
 
int main() {
    unsigned int src; //откуда копируем
    cin >> src;
    float dst; //куда копируем
    memcpy(&dst, &src, sizeof(dst));
    cout << src << endl;
 
    return 0;
}
Вот ассемблерный код функции main, генерируемый с уровнем оптимизации O2
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   0x0000000000400800 <+0>:     sub    $0x18,%rsp
   0x0000000000400804 <+4>:     mov    $0x601080,%edi
   0x0000000000400809 <+9>:     lea    0x4(%rsp),%rsi
   0x000000000040080e <+14>:    mov    %fs:0x28,%rax
   0x0000000000400817 <+23>:    mov    %rax,0x8(%rsp)
   0x000000000040081c <+28>:    xor    %eax,%eax
   0x000000000040081e <+30>:    callq  0x400770 <_ZNSi10_M_extractIjEERSiRT_@plt>
   0x0000000000400823 <+35>:    mov    0x4(%rsp),%esi
   0x0000000000400827 <+39>:    mov    $0x6011a0,%edi
   0x000000000040082c <+44>:    callq  0x4007d0 <_ZNSo9_M_insertImEERSoT_@plt>
   0x0000000000400831 <+49>:    mov    %rax,%rdi
   0x0000000000400834 <+52>:    callq  0x4007e0 <_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@plt>
   0x0000000000400839 <+57>:    mov    0x8(%rsp),%rdx
   0x000000000040083e <+62>:    xor    %fs:0x28,%rdx
   0x0000000000400847 <+71>:    jne    0x400850 <main+80>
   0x0000000000400849 <+73>:    xor    %eax,%eax
   0x000000000040084b <+75>:    add    $0x18,%rsp
   0x000000000040084f <+79>:    retq   
   0x0000000000400850 <+80>:    callq  0x4007c0 <__stack_chk_fail@plt>
Вот это 0x4(%rsp) и 0x8(%rsp), и есть обращение к стеку.

Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Кроме исходного числа, конечно, но оно у нас и так есть, память под него выделена в любом случае.
При компиляции, если выставленн достаточный уровень оптимизации, то выделенная память ограничена регистром и переменная не записывается в стек, этого я и добиваюсь.
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
чем именно не устраивают стандартные способы
Доступ к памяти достаточно дорогая операция, я оптимизирую один алгоритм, хочется убрать выделение памяти для инта при перевеоде его побитово в флоат.
0
3641 / 2978 / 827
Регистрация: 25.03.2012
Сообщений: 10,985
Записей в блоге: 1
12.11.2018, 16:27 9
HardLogin, оптимизация зло.
0
54 / 54 / 2
Регистрация: 20.01.2013
Сообщений: 832
Записей в блоге: 1
12.11.2018, 17:04  [ТС] 10
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
HardLogin, оптимизация зло.
Почему? : )
0
Mental handicap
1245 / 623 / 171
Регистрация: 24.11.2015
Сообщений: 2,429
12.11.2018, 17:15 11
HardLogin,
https://docs.microsoft.com/en-... %3dvs.100)
Есть предположение просто свапать указатели типо:
C++
1
*a++ = *b++;
0
3528 / 2084 / 386
Регистрация: 09.09.2017
Сообщений: 8,532
12.11.2018, 17:31 12
Цитата Сообщение от HardLogin Посмотреть сообщение
Создается. Пруф ниже.
Не создается.
х86 ассемблер мне не особо привычен, поэтому вот на AVR ассемблере:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <avr/io.h>
#include <string.h>
#include <inttypes.h>
 
union inflo{
  uint32_t ival;
  float fval;
};
 
volatile uint32_t x = 1123418112;
volatile float y;
 
int main(){
  //раз
  y = ((union inflo)x).fval;
  //два
  *((volatile uint32_t*)(&y)) = x;
  //три
  memcpy(&y, &x, 4);
}
Код
$ avr-gcc main.c -mmcu=atmega8 -Os -gdwarf-2
$ avr-objdump -S a.out > a.lss
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
int main(){
  //раз
  y = ((union inflo)x).fval;
  5e:   80 91 60 00     lds r24, 0x0060 ; 0x800060 <__data_start>
  62:   90 91 61 00     lds r25, 0x0061 ; 0x800061 <__data_start+0x1>
  66:   a0 91 62 00     lds r26, 0x0062 ; 0x800062 <__data_start+0x2>
  6a:   b0 91 63 00     lds r27, 0x0063 ; 0x800063 <__data_start+0x3>
  6e:   80 93 64 00     sts 0x0064, r24 ; 0x800064 <__data_end>
  72:   90 93 65 00     sts 0x0065, r25 ; 0x800065 <__data_end+0x1>
  76:   a0 93 66 00     sts 0x0066, r26 ; 0x800066 <__data_end+0x2>
  7a:   b0 93 67 00     sts 0x0067, r27 ; 0x800067 <__data_end+0x3>
  //два
  *((volatile uint32_t*)(&y)) = x;
  7e:   80 91 60 00     lds r24, 0x0060 ; 0x800060 <__data_start>
  82:   90 91 61 00     lds r25, 0x0061 ; 0x800061 <__data_start+0x1>
  86:   a0 91 62 00     lds r26, 0x0062 ; 0x800062 <__data_start+0x2>
  8a:   b0 91 63 00     lds r27, 0x0063 ; 0x800063 <__data_start+0x3>
  8e:   e4 e6           ldi r30, 0x64   ; 100
  90:   f0 e0           ldi r31, 0x00   ; 0
  92:   80 83           st  Z, r24
  94:   91 83           std Z+1, r25    ; 0x01
  96:   a2 83           std Z+2, r26    ; 0x02
  98:   b3 83           std Z+3, r27    ; 0x03
  //три
  memcpy(&y, &x, 4);
  9a:   80 91 60 00     lds r24, 0x0060 ; 0x800060 <__data_start>
  9e:   90 91 61 00     lds r25, 0x0061 ; 0x800061 <__data_start+0x1>
  a2:   a0 91 62 00     lds r26, 0x0062 ; 0x800062 <__data_start+0x2>
  a6:   b0 91 63 00     lds r27, 0x0063 ; 0x800063 <__data_start+0x3>
  aa:   80 83           st  Z, r24
  ac:   91 83           std Z+1, r25    ; 0x01
  ae:   a2 83           std Z+2, r26    ; 0x02
  b0:   b3 83           std Z+3, r27    ; 0x03
}

Все три варианта практически идентичны (кроме замены абсолютной адресации на относительную): чтение из ОЗУ в регистры и запись по другому адресу. Стек не используется. Кроме, разумеется, распределения самих переменных.
Цитата Сообщение от HardLogin Посмотреть сообщение
При компиляции, если выставленн достаточный уровень оптимизации, то выделенная память ограничена регистром и переменная не записывается в стек, этого я и добиваюсь.
На Си это невозможно контролировать. Максимум вы можете прописать спецификатор register чтобы подсказать компилятору, но он имеет полное право это проигнорировать.
Так что единственный способ скопировать что-то между регистрами - ассемблерная вставка.
0
54 / 54 / 2
Регистрация: 20.01.2013
Сообщений: 832
Записей в блоге: 1
15.11.2018, 11:00  [ТС] 13
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Не создается.
Ничего не понимаю в AVR ассемблере, но само взятие по адресу переменной (&a) для memcpy, уже означает что у переменной должен быть адрес.
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Все три варианта практически идентичны (кроме замены абсолютной адресации на относительную): чтение из ОЗУ в регистры и запись по другому адресу. Стек не используется. Кроме, разумеется, распределения самих переменных.
Стек используется, а как иначе по вашему локальные переменные в ОЗУ попадают?
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
На Си это невозможно контролировать. Максимум вы можете прописать спецификатор register чтобы подсказать компилятору, но он имеет полное право это проигнорировать.
Так что единственный способ скопировать что-то между регистрами - ассемблерная вставка.
Полностью контролировать конечно невозможно, но в большинстве случаев компиляторы сами неплохо оптимизируют код, и да, если им помочь то они смогут сгенерировать еще более эффективный код. Например компилятор не может соптимзировать сам следующий код:
C++
1
2
3
for (int i = 0; i < a[0]; i++) {
  // ...
}
Но его можно соптимизировать просто присвоив a[0] в локальную переменную, таким образом избавившись от постоянного обращения в ОЗУ
C++
1
2
3
4
int to = a[0];
for (int i = 0; i < to; i++) {
  // ...
}
0
3641 / 2978 / 827
Регистрация: 25.03.2012
Сообщений: 10,985
Записей в блоге: 1
15.11.2018, 12:58 14
HardLogin, потому что во-первых, в современном мире время программиста выходит дороже времени компьютера. Разработано великое множество библиотек, фреймворков, скриптов, скриптовых языков, которые нацелены именно на упрощение программирования по цене процессорного времени. Даже в области работы с чисто С++(а не скриптами, фреймворками итд) от программиста в команде требуется писать в первую очередь читаемый код

- простой и понятный всеми коллегами. Никто не оценит, если ты внезапно начнёшь на работе писать код с асм вставками, SSE инструкциями и.т.д. без особой на то причины. Блин, да что вставки... если ты "для оптимизации" решишь заменить в коде ООП архитектуру и использование boost/STL на свои велосипеды, тебя быстро с прогибом в пол воткнет первый же борцуха-прогер из компании. Ибо ему это в ближайшие несколько лет читать, отлаживать и дополнять.

Во-вторых, низкоуровневая оптимизация типа "а на сколько тактов ++i быстрее i++ ?" - удел компилятора, не программиста. Разработчик компилятора знает заведомо больше всяких хаков на ассемблере, чем программист и может их профессионально воплотить, а пользователь компилятора - программист С++ только всё испортит своими костылями и велосипедами.

В-третьих, если что-то тормозит, надо смотреть свой подход к высокоуровневым алгоритмам. Большую часть кривизны в код приносят именно они. "У тебя программа, что-то ищущая в большом объёме данных? И тормозит линейный цикл с поиском? Не надо в нём лихорадочно делать то же самое только ассемблерной вставкой! Просто используй хеш-таблицу!" И так везде, если о чём и должен программист С++ думать, так о высокоуровневой архитектуре кода и о том, как он взаимодействует с коллегами, а не это ваше "как оптимизировать битики".
0
3528 / 2084 / 386
Регистрация: 09.09.2017
Сообщений: 8,532
15.11.2018, 13:59 15
Цитата Сообщение от HardLogin Посмотреть сообщение
Стек используется, а как иначе по вашему локальные переменные в ОЗУ попадают?
У вас же есть целочисленное значение, которое вы хотите присвоить дробной переменной. Естественно, оно будет храниться либо в коде программы (константа), либо в оперативной памяти. Больше просто негде.
Про то, что в Си нет прямого управления регистрами я уже писал. Переменная может быть туда закеширована, но лишь временно.
Но даже если переменная закеширована, простое обращение к ней через указатель другого типа вполне может быть оптимизировано до работы с теми же регистрами. Зависит от компилятора.
Цитата Сообщение от HardLogin Посмотреть сообщение
Например компилятор не может соптимзировать сам следующий код:
Какие соображения этому мешают?
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
В-третьих, если что-то тормозит, надо смотреть свой подход к высокоуровневым алгоритмам.
Абсолютно согласен. HardLogin, вы пытаетесь оптимизировать одну операцию, которая и без того выполняется за несколько тактов. Если вопрос скорости стоит настолько остро, оптимизацию надо начать с поиска более быстрого алгоритма, потом поиск в нем "бутылочного горлышка" и оптимизировать уже его.
А ускорение интерпретации четырех байт даже в 100 раз (чего все равно не добьетесь) не даст ускорения всей программы даже на 0.1%.
0
54 / 54 / 2
Регистрация: 20.01.2013
Сообщений: 832
Записей в блоге: 1
15.11.2018, 17:50  [ТС] 16
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
...начнёшь на работе писать код с асм вставками, SSE инструкциями и.т.д. без особой на то причины. Блин, ...
И такие причины находятся.
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
Во-вторых, низкоуровневая оптимизация типа "а на сколько тактов ++i быстрее i++ ?" - удел компилятора, не программиста. Разработчик компилятора знает заведомо больше всяких хаков на ассемблере, чем программист и может их профессионально воплотить, а пользователь компилятора - программист С++ только всё испортит своими костылями и велосипедами.
В большинстве практических задач, этого вполне достаточно. Но бывают и такие где уже подобранны лучшие алгоритмы, написано все без лишних фреймворков но все еще медленно, тогда нужно оптимизировать на таком низком уровне как я показал выше.
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
В-третьих, если что-то тормозит, надо смотреть свой подход к высокоуровневым алгоритмам. Большую часть кривизны в код приносят именно они. "У тебя программа, что-то ищущая в большом объёме данных? И тормозит линейный цикл с поиском? Не надо в нём лихорадочно делать то же самое только ассемблерной вставкой! Просто используй хеш-таблицу!" И так везде, если о чём и должен программист С++ думать, так о высокоуровневой архитектуре кода и о том, как он взаимодействует с коллегами, а не это ваше "как оптимизировать битики".
Ответил выше.
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Какие соображения этому мешают?
Потому что массивы хранятся в общедоступной, для потоков одного процесса, памяти. Компилятор не имеет права проводить оптимизации которые могут изменить логику программы. И так как в теории несколько потоков могут иметь доступ к этой ячейке памяти и изменять её, то компилятор постоянно считывает актуальное значение из памяти и сравнивает i с ним.
Цитата Сообщение от COKPOWEHEU Посмотреть сообщение
Абсолютно согласен. HardLogin, вы пытаетесь оптимизировать одну операцию, которая и без того выполняется за несколько тактов. Если вопрос скорости стоит настолько остро, оптимизацию надо начать с поиска более быстрого алгоритма, потом поиск в нем "бутылочного горлышка" и оптимизировать уже его.
А ускорение интерпретации четырех байт даже в 100 раз (чего все равно не добьетесь) не даст ускорения всей программы даже на 0.1%.
Если говорить про практику, то понятное дело, нет смысла так заморочиваться ради функции которая вызывается только на старте. Но если есть ф-ия которая вызывается очень часто и оптимизировать алгоритмически уже не выходит, но нужна максимальная производительность то такие заморочки имеют место быть. И этот ваш 0.1% превратится в в экономию 400 ms за секунду работы проги(много).

Добавлено через 1 минуту
К тому же не стоит рассматривать это с точки зрения практики, меня просто заинтересовал вопрос.
0
3641 / 2978 / 827
Регистрация: 25.03.2012
Сообщений: 10,985
Записей в блоге: 1
15.11.2018, 18:50 17
Цитата Сообщение от HardLogin Посмотреть сообщение
И такие причины находятся.
какие, например? Я вот видел подобные вставки в весьма своеобразных байтомешалках, когда надо софтварно эмулировать алгоритм цифровой обработки сигналов при нормальных условиях запускаемый вообще хардварно на отдельной плате с DSP чипом. Опять же, это из практики. А вот вы, судя по всему говоришь о каком-то сферическом коне в вакууме. уже 20 постов и так и не ясно, что за программа? Да ладно программа... не ясно, с чего бы ты прицепился к этому пресловутому float! Ты хочешь сказать, что перед тобой сейчас лежит программа, которая тормозит при наличии преобразования float и летает без него? Именно практически полезная программа? Ну же, развей мой скепсис, чтобы я перестал относиться к этой теме как к какому-то эзотерическому холивару!
0
3528 / 2084 / 386
Регистрация: 09.09.2017
Сообщений: 8,532
15.11.2018, 23:31 18
Цитата Сообщение от HardLogin Посмотреть сообщение
Потому что массивы хранятся в общедоступной, для потоков одного процесса, памяти.
Ничего подобного. Массив это такая же переменная, он может быть и на стеке в функции.
Цитата Сообщение от HardLogin Посмотреть сообщение
И так как в теории несколько потоков могут иметь доступ к этой ячейке памяти и изменять её
А вот это надо указывать явно модификатором volatile.
Цитата Сообщение от HardLogin Посмотреть сообщение
И этот ваш 0.1% превратится в в экономию 400 ms за секунду работы проги(много).
Во что, во что там у вас превращается ускорение выполнения всей программы на 0.1% ?
Цитата Сообщение от HardLogin Посмотреть сообщение
К тому же не стоит рассматривать это с точки зрения практики, меня просто заинтересовал вопрос.
А на счет теории уже несколько раз было сказано, что простое изменение интерпретации одних и тех же данных процессорного времени не требует вообще.
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
чтобы я перестал относиться к этой теме как к какому-то эзотерическому холивару!
Что плохого в эзотерических холиварах?)
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
15.11.2018, 23:31
Помогаю со студенческими работами здесь

Приведение структуры к uint32_t
Добрый день! Есть переменная пользовательского типа, представляющая собой структуру размером 32...

Что за тип данных uint32_t?
Это без знаковый int, который занимает 32 бита. А вот t, что значит?

Битовые операции между uint32_t и uint8_t
Добрый день. Проблема заключается в следующем: при записи в переменную dat dat &amp;= ~(1&lt;&lt;(i-1));...

uint32_t this decimal constant is unsigned only in ISO C90
Пишу так: uint32_t i=2147483648; Компилятор выдает: this decimal constant is unsigned only in ISO...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
18
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2022, CyberForum.ru