Я вижу здесь два варианта:
Вы можете выбрать необходимый движок при выполнении запроса:
engine1 = create_engine(first_db)
engine2 = create_engine(second_db)
session.execute(drivers, bind=engine1)
Я полагаю, что это переходит к принципам проектирования C и C++. Как Bjarne Stroustrup однажды сказал, один из главных руководящих принципов дизайна C++, "Что Вы не используете, Вы не платите за". И в то время как Dennis Ritchie не мог сказать это в точно тех тех же словах, я полагаю, что это было руководящим принципом, сообщающим его дизайну C (и дизайну C последующими людьми) также. Теперь можно думать, что при выделении памяти, она должна автоматически быть инициализирована к ПУСТОМУ УКАЗАТЕЛЮ, и я был бы склонен соглашаться с Вами. Но который берет циклы машины и если Вы кодируете в ситуации, где каждый цикл очень важен, который не может быть приемлемым компромиссом. В основном C и C++ пытаются остаться вне Вашего пути - следовательно, если Вы хотите что-то, инициализировал Вас, должны сделать это самих.
Как сказано прежде, memset () предлагает желаемую функциональность.
memcpy () для того, чтобы переместить блоки памяти во всех случаях, где источник и целевые буферы не накладываются, или где dest <источник.
memmove () решает случай буферного наложения и dest> источник.
На x86 архитектуре хорошие компиляторы непосредственно заменяют вызовы memset инструкциями по встроенному ассемблерному коду очень эффективно установка памяти целевого буфера, даже применяя дальнейшую оптимизацию как использование 4-байтовых значений для заполнения максимально долго (если следующий код не является полностью синтаксически корректной виной это на моем не использование код сборки X86 в течение долгого времени):
lea edi,dest
;copy the fill byte to all 4 bytes of eax
mov al,fill
mov ah,al
mov dx,ax
shl eax,16
mov ax,dx
mov ecx,count
mov edx,ecx
shr ecx,2
cld
rep stosd
test edx,2
jz moveByte
stosw
moveByte:
test edx,1
jz fillDone
stosb
fillDone:
На самом деле этот код намного более эффективен, чем Ваша версия Z80, поскольку это не делает памяти к памяти, но только регистрируется к перемещениям памяти. Ваш код Z80 является на самом деле настоящим взломом, поскольку он полагается на каждую операцию копии, заполнявшую источник последующей копии.
Если компилятор на полпути хорош, он смог обнаруживать более сложный код C++, который может быть сломан к memset (см. сообщение ниже), но я сомневаюсь, что это на самом деле происходит для вложенных циклов, вероятно, даже вызывая функции инициализации.
memcpy()
должен иметь то поведение. memmove()
не делает дизайном, если блоки перекрытия памяти, он копирует содержание, начинающее в концах буферов избегать такого поведения. Но заполнить буфер определенным значением необходимо использовать memset()
в C или std::fill()
в C++, который большинство современных компиляторов оптимизирует к соответствующей инструкции по заливке блока (такой как ЧЛЕН ПАЛАТЫ ПРЕДСТАВИТЕЛЕЙ STOSB на x86 архитектуре).
Серьезно, если Вы пишете C/C++, просто запишите простой для цикла и позвольте беспокойству компилятора для Вас. Как пример, вот является некоторый код VS2005, сгенерированным для этого точного случая (использующий шаблонный размер):
template <int S>
class A
{
char s_[S];
public:
A()
{
for(int i = 0; i < S; ++i)
{
s_[i] = 'A';
}
}
int MaxLength() const
{
return S;
}
};
extern void useA(A<5> &a, int n); // fool the optimizer into generating any code at all
void test()
{
A<5> a5;
useA(a5, a5.MaxLength());
}
Ассемблерный вывод следующий:
test PROC
[snip]
; 25 : A<5> a5;
mov eax, 41414141H ;"AAAA"
mov DWORD PTR a5[esp+40], eax
mov BYTE PTR a5[esp+44], al
; 26 : useA(a5, a5.MaxLength());
lea eax, DWORD PTR a5[esp+40]
push 5 ; MaxLength()
push eax
call useA
Это больше не становится эффективным, чем это. Прекратите волноваться и доверяйте своему компилятору или по крайней мере взгляните на то, что Ваш компилятор производит прежде, чем попытаться найти способы оптимизировать. Для сравнения я также скомпилировал использование кода std::fill(s_, s_ + S, 'A')
и std::memset(s_, 'A', S)
вместо для цикла и компилятора произвел идентичный вывод.
Существует также calloc, который выделяет и инициализирует память к 0 прежде, чем возвратить указатель. Конечно, calloc только инициализирует к 0, не что-то, что пользователь указывает.
Если Вы играете на аппаратном уровне, то некоторые центральные процессоры имеют контроллеры DMA, которые могут заполнить блоки памяти чрезвычайно быстро (намного быстрее, чем ЦП мог когда-либо делать). Я сделал это на Freescale i. MX21 ЦП.
Если это - самый эффективный способ установить блок памяти к данному значению на Z80, то это довольно возможно это memset()
мог бы быть реализован, как Вы описываете на компиляторе, который предназначается для Z80s.
Это могло бы быть это memcpy()
мог бы также использовать подобную последовательность на том компиляторе.
Но почему был бы компиляторы, предназначающиеся для центральных процессоров с совершенно другими системами команд от Z80, как ожидать, будет использовать идиому Z80 для этих типов вещей?
Помните, что x86 архитектура имеет подобный набор инструкций, которые могли быть снабжены префиксом код операции ЧЛЕНА ПАЛАТЫ ПРЕДСТАВИТЕЛЕЙ, чтобы сделать, чтобы они выполнялись неоднократно, чтобы сделать вещи как копия, заполнить или сравнить блоки памяти. Однако к тому времени, когда Intel выпустил 386 (или возможно это были 486), ЦП на самом деле выполнит те инструкции медленнее, чем более простые инструкции в цикле. Таким образом, компиляторы часто прекращали использовать ОРИЕНТИРОВАННЫЕ ЧЛЕНАМИ ПАЛАТЫ ПРЕДСТАВИТЕЛЕЙ инструкции.
Почему memmove и memcpy ведут себя этот путь?
Вероятно, потому что нет никакого определенного, современного компилятора C++, который предназначается для аппаратных средств Z80? Запишите тот.;-)
Языки не указывают, как данные аппаратные средства реализуют что-либо. Это полностью до программистов компилятора и библиотек. Конечно, запись собственной, очень указанной версии для каждой вообразимой аппаратной конфигурации является большой работой. Это будет причиной.
Там какой-либо разумный путь состоит в том, чтобы сделать этот вид инициализации массива? Там какой-либо разумный путь состоит в том, чтобы сделать этот вид инициализации массива?
Ну, если все остальное перестало работать, Вы могли бы всегда использовать встроенный ассемблерный код. Кроме этого, я ожидаю std::fill
работать лучше всего в хорошей реализации STL. И да, я полностью осведомлен, что мои ожидания являются слишком большими и это std::memset
часто работает лучше на практике.
Z80 упорядочивают Вас, шоу было самым быстрым способом сделать это - в 1978. Это было 30 лет назад. Процессоры прогрессировали много с тех пор, и сегодня это - примерно самый медленный способ сделать это.
Memmove разработан для работы, когда источник и место назначения располагаются перекрытие, таким образом, можно переместить блок вверх памяти на один байт. Это - часть его указанного поведения по стандартам C++ и C. Memcpy является неуказанным; это могло бы работать тождественно к memmove, или это могло бы отличаться, в зависимости от того, как Ваш компилятор решает реализовать его. Компилятор является бесплатным выбрать метод, который более эффективен, чем memmove.
Был более быстрый способ очистить область памяти с помощью стека. Хотя использование LDI и LDIR было очень общим, David Webb (кто продвинул Спектр ZX во всех видах путей как полноэкранные обратные отсчеты числа включая границу), придумал эту технику, которая в 4 раза быстрее:
Объяснение выше было взято от обзора игры David Webbs Starion.
Стандартная программа Z80 могла бы немного походить на это:
DI ; disable interrupts which would write to the stack.
LD HL, 0
ADD HL, SP ; save stack pointer
EX DE, HL ; in DE register
LD HL, 0
LD C, 0x18 ; Screen size in pages
LD SP, 0x4000 ; End of screen
PAGE_LOOP:
LD B, 128 ; inner loop iterates 128 times
LOOP:
PUSH HL ; effectively *--SP = 0; *--SP = 0;
DJNZ LOOP ; loop for 256 bytes
DEC C
JP NZ,PAGE_LOOP
EX DE, HL
LD SP, HL ; restore stack pointer
EI ; re-enable interrupts
Однако та стандартная программа немного менее чем вдвое более быстра. LDIR копирует один байт каждый 21 цикл. Внутренний цикл копирует два байта каждые 24 цикла - 11 циклов для PUSH HL
и 13 для DJNZ LOOP
. Для получения почти в 4 раза более быстрым просто разворачивают внутренний цикл:
LOOP:
PUSH HL
PUSH HL
...
PUSH HL ; repeat 128 times
DEC C
JP NZ,LOOP
Это - очень почти 11 циклов каждые два байта, который приблизительно в 3.8 раза быстрее, чем этот 21 цикл на байт LDIR.
Несомненно, техника много раз переосмысливалась. Например, это появилось ранее в Средстве моделирования Полета подлогики 1 для TRS-80 в 1980.
Это быть выполненным в x86 блоке столь же легко. На самом деле это сводится почти к идентичному коду к Вашему примеру.
mov esi, source ; set esi to be the source
lea edi, [esi + 1] ; set edi to be the source + 1
mov byte [esi], 0 ; initialize the first byte with the "seed"
mov ecx, 100h ; set ecx to the size of the buffer
rep movsb ; do the fill
Однако просто более эффективно установить больше чем один байт за один раз, если Вы можете.
Наконец, memcpy
/memmove
не то, что Вы ищете, это для того, чтобы сделать копии блоков памяти от от области до другого (memmove, позволяет источнику и dest быть частью того же буфера). memset
заполняет блок байтом Вашего выбора.
memmove
и memcpy
не прокладывайте себе путь, потому что это не полезное семантическое для перемещения или копирования памяти. Удобно в Z80 сделать смочь заполнить память, но почему Вы ожидали бы, что функция, названная "memmove", заполнит память единственным байтом? Это для движущихся блоков памяти вокруг. Это реализовано для получения правильного ответа (исходные байты перемещены к месту назначения) независимо от того, как блоки накладываются. Для него полезно получить правильный ответ для перемещения блоков памяти.
Если Вы хотите заполнить память, используйте memset, который разработан, чтобы сделать, что Вы хотите.