Почему там № Z80 как функциональность LDIR в C/C ++/rtl?

Я вижу здесь два варианта:

  1. Вы можете создать 2 создателей сессий здесь и использовать их отдельно в зависимости от движка.
  2. Вы можете выбрать необходимый движок при выполнении запроса:

    engine1 = create_engine(first_db)
    engine2 = create_engine(second_db)
    session.execute(drivers, bind=engine1)
    
5
задан EvilTeach 22 December 2008 в 22:50
поделиться

13 ответов

Я полагаю, что это переходит к принципам проектирования C и C++. Как Bjarne Stroustrup однажды сказал, один из главных руководящих принципов дизайна C++, "Что Вы не используете, Вы не платите за". И в то время как Dennis Ritchie не мог сказать это в точно тех тех же словах, я полагаю, что это было руководящим принципом, сообщающим его дизайну C (и дизайну C последующими людьми) также. Теперь можно думать, что при выделении памяти, она должна автоматически быть инициализирована к ПУСТОМУ УКАЗАТЕЛЮ, и я был бы склонен соглашаться с Вами. Но который берет циклы машины и если Вы кодируете в ситуации, где каждый цикл очень важен, который не может быть приемлемым компромиссом. В основном C и C++ пытаются остаться вне Вашего пути - следовательно, если Вы хотите что-то, инициализировал Вас, должны сделать это самих.

8
ответ дан 18 December 2019 в 05:12
поделиться

Как сказано прежде, 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 (см. сообщение ниже), но я сомневаюсь, что это на самом деле происходит для вложенных циклов, вероятно, даже вызывая функции инициализации.

-1
ответ дан 18 December 2019 в 05:12
поделиться

memcpy() должен иметь то поведение. memmove() не делает дизайном, если блоки перекрытия памяти, он копирует содержание, начинающее в концах буферов избегать такого поведения. Но заполнить буфер определенным значением необходимо использовать memset() в C или std::fill() в C++, который большинство современных компиляторов оптимизирует к соответствующей инструкции по заливке блока (такой как ЧЛЕН ПАЛАТЫ ПРЕДСТАВИТЕЛЕЙ STOSB на x86 архитектуре).

1
ответ дан 18 December 2019 в 05:12
поделиться

Серьезно, если Вы пишете 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) вместо для цикла и компилятора произвел идентичный вывод.

2
ответ дан 18 December 2019 в 05:12
поделиться

Если Вы находитесь на PowerPC, _dcbz ().

2
ответ дан 18 December 2019 в 05:12
поделиться

Существует также calloc, который выделяет и инициализирует память к 0 прежде, чем возвратить указатель. Конечно, calloc только инициализирует к 0, не что-то, что пользователь указывает.

2
ответ дан 18 December 2019 в 05:12
поделиться

Если Вы играете на аппаратном уровне, то некоторые центральные процессоры имеют контроллеры DMA, которые могут заполнить блоки памяти чрезвычайно быстро (намного быстрее, чем ЦП мог когда-либо делать). Я сделал это на Freescale i. MX21 ЦП.

4
ответ дан 18 December 2019 в 05:12
поделиться

Если это - самый эффективный способ установить блок памяти к данному значению на Z80, то это довольно возможно это memset() мог бы быть реализован, как Вы описываете на компиляторе, который предназначается для Z80s.

Это могло бы быть это memcpy() мог бы также использовать подобную последовательность на том компиляторе.

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

Помните, что x86 архитектура имеет подобный набор инструкций, которые могли быть снабжены префиксом код операции ЧЛЕНА ПАЛАТЫ ПРЕДСТАВИТЕЛЕЙ, чтобы сделать, чтобы они выполнялись неоднократно, чтобы сделать вещи как копия, заполнить или сравнить блоки памяти. Однако к тому времени, когда Intel выпустил 386 (или возможно это были 486), ЦП на самом деле выполнит те инструкции медленнее, чем более простые инструкции в цикле. Таким образом, компиляторы часто прекращали использовать ОРИЕНТИРОВАННЫЕ ЧЛЕНАМИ ПАЛАТЫ ПРЕДСТАВИТЕЛЕЙ инструкции.

2
ответ дан 18 December 2019 в 05:12
поделиться

Почему memmove и memcpy ведут себя этот путь?

Вероятно, потому что нет никакого определенного, современного компилятора C++, который предназначается для аппаратных средств Z80? Запишите тот.;-)

Языки не указывают, как данные аппаратные средства реализуют что-либо. Это полностью до программистов компилятора и библиотек. Конечно, запись собственной, очень указанной версии для каждой вообразимой аппаратной конфигурации является большой работой. Это будет причиной.

Там какой-либо разумный путь состоит в том, чтобы сделать этот вид инициализации массива? Там какой-либо разумный путь состоит в том, чтобы сделать этот вид инициализации массива?

Ну, если все остальное перестало работать, Вы могли бы всегда использовать встроенный ассемблерный код. Кроме этого, я ожидаю std::fill работать лучше всего в хорошей реализации STL. И да, я полностью осведомлен, что мои ожидания являются слишком большими и это std::memset часто работает лучше на практике.

6
ответ дан 18 December 2019 в 05:12
поделиться

Z80 упорядочивают Вас, шоу было самым быстрым способом сделать это - в 1978. Это было 30 лет назад. Процессоры прогрессировали много с тех пор, и сегодня это - примерно самый медленный способ сделать это.

Memmove разработан для работы, когда источник и место назначения располагаются перекрытие, таким образом, можно переместить блок вверх памяти на один байт. Это - часть его указанного поведения по стандартам C++ и C. Memcpy является неуказанным; это могло бы работать тождественно к memmove, или это могло бы отличаться, в зависимости от того, как Ваш компилятор решает реализовать его. Компилятор является бесплатным выбрать метод, который более эффективен, чем memmove.

5
ответ дан 18 December 2019 в 05:12
поделиться

Был более быстрый способ очистить область памяти с помощью стека. Хотя использование LDI и LDIR было очень общим, David Webb (кто продвинул Спектр ZX во всех видах путей как полноэкранные обратные отсчеты числа включая границу), придумал эту технику, которая в 4 раза быстрее:

  • сохраняет Указатель вершины стека и затем перемещает его в конец экрана.
  • ЗАГРУЖАЕТ пару регистра HL нулем,
  • входит в крупный цикл, ПРОДВИГАЯ HL на Стек.
  • Стек перемещает экран вверх и вниз через память и в процессе, очищает экран.

Объяснение выше было взято от обзора игры 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.

11
ответ дан 18 December 2019 в 05:12
поделиться

Это быть выполненным в 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 заполняет блок байтом Вашего выбора.

3
ответ дан 18 December 2019 в 05:12
поделиться

memmove и memcpy не прокладывайте себе путь, потому что это не полезное семантическое для перемещения или копирования памяти. Удобно в Z80 сделать смочь заполнить память, но почему Вы ожидали бы, что функция, названная "memmove", заполнит память единственным байтом? Это для движущихся блоков памяти вокруг. Это реализовано для получения правильного ответа (исходные байты перемещены к месту назначения) независимо от того, как блоки накладываются. Для него полезно получить правильный ответ для перемещения блоков памяти.

Если Вы хотите заполнить память, используйте memset, который разработан, чтобы сделать, что Вы хотите.

12
ответ дан 18 December 2019 в 05:12
поделиться