Страницы

Поиск по вопросам

воскресенье, 29 марта 2020 г.

Оптимизация работы с памятью в С++

#cpp #память


решил написать свой .obj парсер, и столкнулся с небольшой проблемой...

...

std::vector> vertices;

std::string line;
std::ifstream inputStream(objFileName);
while (std::getline(inputStream, line))
{
    if (line.substr(0, 2) == "v ")
    {
        std::istringstream stream(line.substr(2));
        float x, y, z;
        stream >> x;
        stream >> y;
        stream >> z;
        vertices.push_back({ x, y, z, 1.0f });
    }

    ... // Texture coordinate, normals e.t.c.
}


Вопрос заключается в следующем, можно ли как-то оптимизировать в данном случае работу
с памятью, но так сказать не во вред удобности? При substr постоянно же будет аллоцироваться
новая строка, да и при создании стрима. Хотелось бы как-то ограничиться парой выделений
памяти и все на этом, а не фрагментировать кучу такими маленькими выделениями(вектор
в дальнейшем будет заменен, так как тоже очень затратно будет происходить реаллоцирование
памяти, пока просто хотел бы разобраться со строками)... 
    


Ответы

Ответ 1



В современном С++ всю работу с константными/немодифиуируемыми [под]строками имеет смысл переводить на использование std::string_view. То есть везде, где в вашей программе явно или концептуально выступает const std::string & он должен быть заменен на const std::string_view &. Это относится и к вашему применению substr. Нет никаких причин формировать целый новый std::string объект только ради выделения немодифицируемой подстроки. По уму и std::string::substr должен был бы возвращать std::string_view, а не std::string, но так исторически сложилось и сейчас уже не переделать. В данном случае можно предложить заменить все line.substr(i, j) на std::string_view(line).substr(i, j) Это избавит вас от ненужных промежуточных std::string объектов и сопутствующего выделения памяти. Или уже с самого начала вы могли после получения line сразу сформировать std::string_view line_view = line; и дальше работать исключительно с line_view. Это, однако, не избавит вас от выделения памяти при инициализации std::istringstream и внутри std::istringstream. Здесь бы бы полезен парсер для std::string_view, но готового в стандартной библиотеке нет (кроме sscanf). Сама стандартная библиотека еще толком не перешла на использование std::string_view. Ваше vertices.push_back({ x, y, z, 1.0f }); это тоже потенциально - ненужное копирование. Возможно, что лучше vertices.emplace_back(x, y, z, 1.0); но это уже зависит от свойств Math::Vector4.

Как получить область видимости с одной функции в другую?

#javascript


Как получить область видимости[scope] LexicalEnvironment с одной функции в другую?



function number() {
  var a=5;
  var b=5;
  var c=5
}
number();

function summa() {

  return a+b+c;
}
var d = summa();
console.log(d);



    


Ответы

Ответ 1



Если очень хочется, то можно вот так. Главное не потерять new, иначе this станет window и переменные объявятся как глобальные function number() { this.a=5; this.b=5; this.c=5 return this; } function summa() { return this.a+this.b+this.c; } console.log(summa.call(new number()));

Выбрать только уникальные массивы (и с большей длинной) из многомерного массива

#php #массивы


Пример массива:

array(
    0 => array(
        'alias' => 'test',
        'title' => 'stack',
        'content' => 'overflow',
        'year' => 2019,
        'test' => null
    ),

    1 => array(
        'alias' => null,
        'title' => 'stack',
        'content' => 'overflow',
        'year' => null,
        'test' => null
    ),

    2 => array(
        'alias' => 'else',
        'title' => 'demo',
        'content' => 'stack_overflow',
        'year' => 2020,
    )
);


Уникальным ключом является title, выходит массивы с индексами 0, 1, являются дублями,
таких массивов может быть очень много...

Мне нужно оставить только тот массив в котором меньше всего значений null, то есть
в данном случае массив с индексом 1 нужно удалить, так-как это дубль массива с индексом
0 и т.д. Таких дублей может быть разное количество.

Думал сделать что-то подобное:

if(strlen(serialize($array))) {
    //Пишем массив который длиннее 
}


Но не могу придумать как найти совпадения...
    


Ответы

Ответ 1



В итоге написал такую функцию: public function removeArrayDuplicates($array, $index_key) { $result = array_reverse( array_values( array_column( array_reverse($array), null, $index_key) ) ); return $result; } var_dump(removeArrayDuplicates($array, $title)); Получаю массивы без дублей и с нужными значениями. UPDATE: Всё таки не с нужными значениями. Данная функция не сработала как ожидалось поэтому нагородил другую. Эта точно работает как нужно. Может кому то будет полезно, либо сможете улучшить мой ответ. function removeArrayDuplicates($array, $search_key) { $stored_duplicates = array(); $max_array_length = null; $current_array = null; $result = array(); foreach($array as $item) { $stored_duplicates[$item[$search_key]][] = $item; } foreach($stored_duplicates as $arrays) { foreach($arrays as $item) { if(count($arrays) > 1) { $array_length = strlen(serialize($item)); if($array_length > $max_array_length) { $max_array_length = $array_length; $current_array = $item; } } else { $result[] = $item; } } if($current_array) { $result[] = $current_array; $max_array_length = null; $current_array = null; } } return $result; }

Считать jpg по 8 бит, по 16 бит, по 24 бита

#python


Имеется jpg файл. Хочу получить список битовых цепочек файла по 8, по 16 и по 24
бита, используя python. В дальнейшем хочу проанализировать распределение вероятности
появления каждого из исходов для данных трех размерностей.

На данный момент разобрался, как получить список из десятичных чисел по 8 бит:

with open("1.jpg", "rb") as imageFile:

f = imageFile.read()
b = list(bytearray(f))


Каким образом можно сделать то же самое для цепочек из 16 и 24 бит?
    


Ответы

Ответ 1



Так как вы уже начитали ваш файл до битовой цепочки f, вы можете её использовать как псевдо-файл и читать по 2 (или по 3) байта из неё в цикле: import io f_as_file = io.BytesIO(f) c = [] while True: part = f_as_file.read(2) # или 3 if part: c.append(part) else: break или с применением абстракции списка (list comprehension) (вместо инициализации списка c и добавления к нему в цикле): import io BYTES = 2 # или 3 f_as_file = io.BytesIO(f) c = [f_as_file.read(BYTES) for __ in range(len(f) // BYTES)]

Ответ 2



Так как вы уже начитали ваш файл до битовой цепочки f, вы можете из её сделать 2- или 3-байтовые отрезки: BYTES_COUNT = 2 # или 3, или даже 1 - вместо вашего b = list(bytearray(f)) c = [f[i:i+BYTES_COUNT] for i in range(0, len(f), BYTES_COUNT)] Тест: Пусть f = b'\0\1\2\3\4\5\6\7\x08\x09\x0A\x0B'. Тогда для BYTES_COUNT = 2 будет вывод команды print(c) такой: [b'\x00\x01', b'\x02\x03', b'\x04\x05', b'\x06\x07', b'\x08\t', b'\n\x0b'] и для BYTES_COUNT = 3 такой: [b'\x00\x01\x02', b'\x03\x04\x05', b'\x06\x07\x08', b'\t\n\x0b'] Примечание: b'x09' то-же самое как b'\t' (табулятор) и b'x0a' то-же самое как b'\n' (новая строка).

функция использует замыкания?

#javascript




function Article() {
  Article.count++;

  //...
}
Article.count = 0;

Article.showCount = function() {
  alert( this.count ); // (1)
}

// использование
new Article();
new Article();
Article.showCount(); // (2)



    


Ответы

Ответ 1



Замыкания в этом коде нет. Функция в js - это объект. У этого объекта Вы создали свойство Article.count = 0; При вызове этой функции (не важно, как конструктора или нет) значение этого свойства увеличивается. При вызове Article.showCount(); this внутри showCount это функция/объект Article, у которого есть созданное Вами свойство count.

QT C++ работа с базой данных в разных потоках

#cpp #qt


Как можно модифицировать ПО для мультипоточной работы. 

Хотелось бы увидеть минимальный базовый пример, дабы разобраться и пока не лезть в дебри.

Есть большая таблица(100 тыс наименований) при их запросе виснет GUI.
Хотелось бы это устранить.

Логика была такая.


Человек открывает программу.
Конструктор формы отправил запрос потоку в БД.
Строится каркас приложения.
Берём данные из потока в основной.
Заполняем полученными данными таблицу.


p.s
Пробовал сделать подобное на сигналах и слотах с применением QThread. Отправлял запрос
в другой поток, но он начинал работать только когда закроется основное приложение...



mainwindow.СPP

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    //QSqlDatabase objDatabase;
    objDatabase = QSqlDatabase::addDatabase("QMYSQL");
    objDatabase.setDatabaseName("librarydb");
    objDatabase.setHostName("127.0.0.1");
    objDatabase.setPort(3306);
    objDatabase.setUserName("hays0503");
    objDatabase.setPassword("hays0503");
    objDatabase.open();

    //QSqlTableModel *objTableModel;
    objTableModel = new QSqlTableModel();
    objTableModel->setTable("author");
    if (!objTableModel->select())
    {
        qDebug()<<"Error";
    }else {
       ui->tableView->setModel(objTableModel);
    }
}


mainwindow.H

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include 
#include 

#include 
#include 
#include 
#include 

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

private:
    Ui::MainWindow *ui;
    QSqlDatabase objDatabase;
    QSqlTableModel *objTableModel;
};

#endif // MAINWINDOW_H

    


Ответы

Ответ 1



void MainWindow::on_pushButton_clicked() { QFuture future = QtConcurrent::run( [this]() { // Исполняем этот код в другом потоке //QSqlDatabase objDatabase; objDatabase = QSqlDatabase::addDatabase("QMYSQL"); objDatabase.setDatabaseName("librarydb"); objDatabase.setHostName("127.0.0.1"); objDatabase.setPort(3306); objDatabase.setUserName("hays0503"); objDatabase.setPassword("hays0503"); objDatabase.open(); //QSqlTableModel *objTableModel; objTableModel = new QSqlTableModel(this); objTableModel->setTable("author"); if (!objTableModel->select()) { delete objTableModel; objTableModel = nullptr; qDebug()<<"Error"; } return objTableModel; }); QFutureWatcher *watcher = new QFutureWatcher(this); connect(watcher, SIGNAL(finished()), watcher, [this, watcher]() { // Запускаем этот код в UI потоке, когда объект future завершит свою работу в рабочем потоке ui->tableView->setModel(watcher->result()); watcher->deleteLater(); // Удалим ненужный watcher }, Qt::QueuedConnection); ); watcher->setFuture(future); // Связываем watcher с feature. Это быстрая операция и не тормозит поток UI } Если много раз кликать на кнопку, то будет запущено сразу много одинаковых задач. Надо не забывать, что работать с объектами objDatabase и objTableModel можно только из одного потока одновременно.

Эмуляция COM порта

#linux #ubuntu #com_port


Есть ubuntu 16.04 которая установлена на VirtualBox. В самой машине есть один физический
com порт, он прокинут в VirtualBox в режиме "хост устройство" вот так:



Еще я включил 2 порта в режиме "отключен":



В результате на ubuntu эти порты определяются. (Но работать я так понимаю могу только
с одним)

 $ dmesg | grep tty
[    0.004000] console [tty0] enabled
[    0.772403] 00:02: ttyS0 at I/O 0x3f8 (irq = 4, base_baud = 115200) is a 16550A
[    0.794717] 00:03: ttyS1 at I/O 0x2f8 (irq = 3, base_baud = 115200) is a 16550A
[    0.816355] 00:04: ttyS2 at I/O 0x3e8 (irq = 4, base_baud = 115200) is a 16550A


Вопрос такой: могу ли я сделать так чтобы я мог писать в com-порт и читать из него(как
здесь), не подключая никакое устройство (мне просто нужно эхо) и не прибегая ни к каким
костылям вроде вставки скрепки в порт что бы замкнуть TX-RX. 

Может ли мне в этом помочь socat. Насколько я понял, с помощью  него можно сделать
мост с одного порта на другой, но я запутался в дебрях его настройки, был бы благодарен
если кто подскажет правильную строчку. 

Или socat для этой цели не подходит, и мне нужно что-то другое?
    


Ответы

Ответ 1



Последовательный порт с точки зрения *nix-систем — это обычное терминальное устройство. Так что самое простое — создать псевдотерминал и подключиться к нему: socat PIPE PTY,link=/tmp/my_pty,raw,echo=0 PIPE — создаёт неименованную трубу между входом и выходом, по сути работает как простой эхо-ответчик PTY — создаёт псевдотерминальное устройство, к которому можно подключиться link=/tmp/my_pty — создаёт ссылку на устройство, к которой можно подключаться, как к устройству, именно его можно указывать в open() при открытии порта. raw,echo=0 — задаёт режим работы псевдотерминала, дабы исключить изменение данных ответа. Из отличий от настоящего порта, самое явное — отсутствие задержки при отправке/получении данных, независимо от установленного baudrate'а данные будут передаваться почти мгновенно и целым пакетом. Из альтернатив: tty0tty — эмулирует полноценную пару соединённых друг с другом портов, в качестве эхо можно использовать тот же socat на одном из них. Перенаправить COM-порт в коробке на unix или tcp сокет. После чего можно использовать на этом сокете любой эхо-клиент/сервер на хосте или даже удалённой системе. В качестве клиента/сервера может выступать тот же socat.