Зачем может понадобиться такой код?
От: GarryIV  
Дата: 09.08.03 08:24
Оценка:
сорри в первый раз ошибка закралась — правильно так

задачка из третьего издания Страуструпа (§6.6 задача 15)

void send(int *to,int *from,int count)
{
    int n = (count+7)/8;
    switch(count%8)
    {
    case 0:    do{ *to++ = *from++;
    case 7:        *to++ = *from++;
    case 6:        *to++ = *from++;
    case 5:        *to++ = *from++;
    case 4:        *to++ = *from++;
    case 3:        *to++ = *from++;
    case 2:        *to++ = *from++;
    case 1:        *to++ = *from++;
                } while(--n>0);
    }
}


второй день покоя не дает.
WBR, Igor Evgrafov
Re: Зачем может понадобиться такой код?
От: Blazkowicz Россия  
Дата: 09.08.03 08:44
Оценка:
Здравствуйте, GarryIV, Вы писали:

GIV>сорри в первый раз ошибка закралась — правильно так


GIV>задачка из третьего издания Страуструпа (§6.6 задача 15)


GIV>
GIV>void send(int *to,int *from,int count)
GIV>{
GIV>    int n = (count+7)/8;
GIV>    switch(count%8)
GIV>    {
GIV>    case 0:    do{ *to++ = *from++;
GIV>    case 7:        *to++ = *from++;
GIV>    case 6:        *to++ = *from++;
GIV>    case 5:        *to++ = *from++;
GIV>    case 4:        *to++ = *from++;
GIV>    case 3:        *to++ = *from++;
GIV>    case 2:        *to++ = *from++;
GIV>    case 1:        *to++ = *from++;
GIV>                } while(--n>0);
GIV>    }
GIV>}
GIV>


GIV>второй день покоя не дает.

Я конечно могу ошибаться, но кажется что-то типа разворачивания цикла... ускоряет его исполнение.
Re: Зачем может понадобиться такой код?
От: Alexmoon Украина  
Дата: 09.08.03 11:38
Оценка:
Здравствуйте, GarryIV, Вы писали:

GIV>сорри в первый раз ошибка закралась — правильно так

ошибки бывают у всех без исключения, только у всех разные разной степени важности — это опыт, сын ошибок трудных.

GIV>задачка из третьего издания Страуструпа (§6.6 задача 15)

тут для пущей ясности стоит обратить еще внимание на упражнение номер 9.

не скрою. очень стало интересно, потому что Страуструпа вроде бы прочитал, но примера не помню.
Но вроде бы понял суть. Конечно же и я могу ошибаться, но надеюсь не сильно Это я сам себя подбадриваю.

void send(int *to,int *from,int count)
{
    //количество проходов цикла - +7 я рассматриваю только как округление в большую сторону
        int n = (count+7)/8;

        //остаток от деления. понятно что значений может быть только 8 от 0 до 7
        //раз все перечислены, то понятно, что обойти этот блок не удастся.
        //насчет цикла - здесь ускорение может произойти разве что на первом вхождении и то если count%8 не равен нулю.
        //case - это не что иное как метка, поэтому степень вложения значения не имеет. разве что если while(--n>0) написать в начале,
        //       то мы сделаем одну лишнюю итерацию, если count не будет кратен 8.
    switch(count%8)
    {
    case 0:    do{ *to++ = *from++;
    //вот тут мы и подошли к самому главному. в 9-ом упражнении он явно просит правильно расставить приоритеты
        //выполнения операций, потому что при такой записи могут быть проблемы, если не учесть оные. Потому что согласно принятым стандартом приоритетами выполнения операций
        //сначала будет инкрементирован указатель, а потом будет разыменован и по адресу на который указывает будет занесено значение. по этому (*XXX), а потом все остальное, но в принципе все по желанию.

        //при данной конструкции кода если количество операций не очень велико, то просто произойдет присвоение (*to)=(*from) по внешним переданным адресам,
        //но все равно CRT в конце концов свалится, потому что если в начале после int n = count%8; не сделать to = new int[n+8]; то стек потока будет нарушен
        //если сделать еще один финт хвостом типа просто *++to = *from++;, то внешние переменные не изменят значений, а зрачки у нас потом в размерах все равно вырастут.
        case 7:        *to++ = *from++;
    case 6:        *to++ = *from++;
    case 5:        *to++ = *from++;
    case 4:        *to++ = *from++;
    case 3:        *to++ = *from++;
    case 2:        *to++ = *from++;
    case 1:        *to++ = *from++;
                } while(--n>0);
    }
}

надеюсь ничего не забыл. Он спрашивает о том, кому может понадобиться такой код. Именно такой даже не знаю, а после некоторых не сложных преобразований кому угодно. фамилий называть не буду.

GIV>второй день покоя не дает.

желаю спокойствия и умиротворения.

Удачи. Рад был помочь, если был прав.
Re[2]: Зачем может понадобиться такой код?
От: GarryIV  
Дата: 09.08.03 20:47
Оценка:
Здравствуйте, Alexmoon, Вы писали:

A>Здравствуйте, GarryIV, Вы писали:


GIV>>сорри в первый раз ошибка закралась — правильно так

A>ошибки бывают у всех без исключения, только у всех разные разной степени важности — это опыт, сын ошибок трудных.

Спасибо за поддержку

A>надеюсь ничего не забыл. Он спрашивает о том, кому может понадобиться такой код. Именно такой даже не знаю, а после некоторых не сложных преобразований кому угодно. фамилий называть не буду.


То как это безобразие работает вопросов как раз не вызывает — я даже для проверки набил и проверил себя. А вот зачем (кому) такое может понадобится? Меня смущает прыжок внутрь цикла. ИМХО это только затрудняет понимание текста и не имеет никаких преимуществ перед простым циклом.
while(--k>0) *to++=*from++;

Некоторое ускорение работы за счет развертывания цикла для меня не кажется весомым аргументом...

GIV>>второй день покоя не дает.

A>желаю спокойствия и умиротворения.
A>Удачи. Рад был помочь, если был прав.

И все таки в какой реальной ситуации подобный код может применяться? Или какой ответ имел ввиду Страуструп задавая этот вопрос?

PS: Я как то в школе написал похожую конструкцию на фокале (прикололся) — препод долго чесал репу, поставил "5" а остальным сказал чтоб не смели так писать.
WBR, Igor Evgrafov
Re: Зачем может понадобиться такой код?
От: Андрей Тарасевич Беларусь  
Дата: 09.08.03 21:25
Оценка: 12 (2)
Здравствуйте, GarryIV, Вы писали:

GIV>задачка из третьего издания Страуструпа (§6.6 задача 15)


GIV>
GIV>void send(int *to,int *from,int count)
GIV>{
GIV>    int n = (count+7)/8;
GIV>    switch(count%8)
GIV>    {
GIV>    case 0:    do{ *to++ = *from++;
GIV>    case 7:        *to++ = *from++;
GIV>    case 6:        *to++ = *from++;
GIV>    case 5:        *to++ = *from++;
GIV>    case 4:        *to++ = *from++;
GIV>    case 3:        *to++ = *from++;
GIV>    case 2:        *to++ = *from++;
GIV>    case 1:        *to++ = *from++;
GIV>                } while(--n>0);
GIV>    }
GIV>}
GIV>


Развертка цикла. Делается для оптиизации критического по времени выполнения кода.
Best regards,
Андрей Тарасевич
Re[2]: Зачем может понадобиться такой код?
От: GarryIV  
Дата: 10.08.03 09:45
Оценка:
Здравствуйте, Андрей Тарасевич, Вы писали:

GIV>>задачка из третьего издания Страуструпа (§6.6 задача 15)

....
АТ>Развертка цикла. Делается для оптиизации критического по времени выполнения кода.

Да Вы оказались правы это может сильно ускорить выполнение.

Однако заметил непонятную для меня вещь

массив на куче:
=======================
3. elapsed 3415 ' memcpy
2. elapsed 5888 ' банальный цикл
1. elapsed 3605 ' страуструп
=======================

массив в стеке
=======================
3. elapsed 3766 ' memcpy
2. elapsed 3765 ' банальный цикл
1. elapsed 3786 ' страуструп
=======================


Почему 2 сильно быстрее в случае стека а 1 и 3 немного змедляются?
Компилятор VC++ 7.1

#include "stdafx.h"
#include <iostream>

// из задачки Страуструпа
void send(int *to,int *from,int count)
{
    int n = (count+7)/8;
    switch(count%8)
    {
    case 0:    do{ *to++ = *from++;
    case 7:        *to++ = *from++;
    case 6:        *to++ = *from++;
    case 5:        *to++ = *from++;
    case 4:        *to++ = *from++;
    case 3:        *to++ = *from++;
    case 2:        *to++ = *from++;
    case 1:        *to++ = *from++;
            } while(--n>0);
    }
}
// То же самое но покороче
void send2(int *to,int *from,int count)
{
    while(count-- > 0)*to++=*from++;
}
// То же самое но memcpy
void send3(int *to,int *from,int count)
{
    memcpy(to,from,sizeof(int)*count);
}
int _tmain(int argc, _TCHAR* argv[])
{
    const int N=30000;
    // массив в стеке
    int a[N];
    int b[N];

    // массив на куче
    //int *a = new int[N];
    //int *b = new int[N];

    // инициализируем массив чем в голову пришло
    for(int i=0;i<N;i++) 
        b[i]=i; 

    DWORD d=0;
    // прогоняем несколько раз для устаканивания результатов
        for(int j=0;j<5;j++) 
    {
        std::cout << "=======================" << std::endl;
        d = GetTickCount();

        
        for(int i=0;i<N;i++) 
        {
            // пришлось пихать туда обратно а то компилятор
            // цикл вообще выкидывал
            send3(a,b,N);
            send3(b,a,N);
        }
        std::cout << "3. elapsed " << (GetTickCount()-d)<<std::endl;
        
        d = GetTickCount();
        for(int i=0;i<N;i++) 
        {
            send2(a,b,N);
            send2(b,a,N);
        }
        std::cout << "2. elapsed " << (GetTickCount()-d)<<std::endl;

        d = GetTickCount();
        for(int i=0;i<N;i++) 
        {
            send(a,b,N);
            send(b,a,N);
        }
        std::cout << "1. elapsed " << (GetTickCount()-d)<<std::endl;
        std::cout << "=======================" << std::endl;
    }
    char c;
    std::cin >> c;

//    delete[] a;
//    delete[] b;
    return 0;


}
WBR, Igor Evgrafov
Re[3]: Зачем может понадобиться такой код?
От: King Oleg Украина http://kingoleg.livejournal.com
Дата: 11.08.03 14:43
Оценка:
Здравствуйте, GarryIV, Вы писали:

GIV>То как это безобразие работает вопросов как раз не вызывает — я даже для проверки набил и проверил себя. А вот зачем (кому) такое может понадобится? Меня смущает прыжок внутрь цикла. ИМХО это только затрудняет понимание текста и не имеет никаких преимуществ перед простым циклом.

GIV>
GIV>while(--k>0) *to++=*from++;
GIV>

Там break не стоит. И вообще не понятно, как этот код исполняется. Ведь, если count%8 != 0, то исполнение прыгает в цикл do минуя его (do)
King Oleg
*Читайте DOC'и, они rules*
Re[4]: Зачем может понадобиться такой код?
От: GarryIV  
Дата: 12.08.03 09:22
Оценка:
Здравствуйте, King! Вы писали:

KO> Здравствуйте, GarryIV, Вы писали:


GIV>> То как это безобразие работает вопросов как раз не вызывает — я даже

GIV>> для проверки набил и проверил себя. А вот зачем (кому) такое может
GIV>> понадобится? Меня смущает прыжок внутрь цикла. ИМХО это только
GIV>> затрудняет понимание текста и не имеет никаких преимуществ перед
GIV>> простым циклом.
 while(--k>0) *to++=*from++;

KO> Там break не стоит. И вообще не понятно, как этот код исполняется.
KO> Ведь, если count%8 != 0, то исполнение прыгает в цикл do минуя его
KO> (do)

Не нужен там break вот он и не стоит — этот код копирует count int'ов из одного массива в другой (аномалию при count<=0 не рассматриваем, Страуструп специально ошибки допускает в примерах). А do это всего лишь метка. Можешь сам скопировать да прогнать под отладчиком + посмотреть на asm.
Posted via RSDN NNTP Server 1.7 beta
WBR, Igor Evgrafov
Re[3]: Зачем может понадобиться такой код?
От: kulentsov  
Дата: 13.08.03 11:09
Оценка:
GIV>То как это безобразие работает вопросов как раз не вызывает — я даже для проверки набил и проверил себя. А вот зачем (кому) такое может понадобится? Меня смущает прыжок внутрь цикла. ИМХО это только затрудняет понимание текста и не имеет никаких преимуществ перед простым циклом.
GIV>
GIV>while(--k>0) *to++=*from++;
GIV>

GIV>Некоторое ускорение работы за счет развертывания цикла для меня не кажется весомым аргументом...
Именно ускорение, и это может быть весомым аргументом. В простом случае проверка и прыжок выполняется после пересылки каждого байта, а в примере — после каждого восьмого байта. Как сейчас — не знаю, а в старых процессорах (помните — там еще очередь команд чистилась после каждого джампа ) ускорение было значительным.
Re[4]: Зачем может понадобиться такой код?
От: GarryIV  
Дата: 13.08.03 12:10
Оценка:
Здравствуйте, kulentsov! Вы писали:

GIV>> То как это безобразие работает вопросов как раз не вызывает — я даже

GIV>> для проверки набил и проверил себя. А вот зачем (кому) такое может
GIV>> понадобится? Меня смущает прыжок внутрь цикла. ИМХО это только
GIV>> затрудняет понимание текста и не имеет никаких преимуществ перед
GIV>> простым циклом.
 while(--k>0) *to++=*from++;
Некоторое

GIV>> ускорение работы за счет развертывания цикла для меня не кажется
GIV>> весомым аргументом...
k> Именно ускорение, и это может быть весомым аргументом. В простом
k> случае проверка и прыжок выполняется после пересылки каждого байта, а в
k> примере — после каждого восьмого байта. Как сейчас — не знаю, а в старых
k> процессорах (помните — там еще очередь команд чистилась после каждого
k> джампа ) ускорение было значительным.

Да, я уже провел тесты — в определенных ситуациях существенно быстрее.
Posted via RSDN NNTP Server 1.7 beta
WBR, Igor Evgrafov
Re: Зачем может понадобиться такой код?
От: Аноним  
Дата: 16.08.03 09:09
Оценка:
Здравствуйте, GarryIV, Вы писали:

GIV>сорри в первый раз ошибка закралась — правильно так


GIV>задачка из третьего издания Страуструпа (§6.6 задача 15)


GIV>
GIV>void send(int *to,int *from,int count)
GIV>{
GIV>    int n = (count+7)/8;
GIV>    switch(count%8)
GIV>    {
GIV>    case 0:    do{ *to++ = *from++;
GIV>    case 7:        *to++ = *from++;
GIV>    case 6:        *to++ = *from++;
GIV>    case 5:        *to++ = *from++;
GIV>    case 4:        *to++ = *from++;
GIV>    case 3:        *to++ = *from++;
GIV>    case 2:        *to++ = *from++;
GIV>    case 1:        *to++ = *from++;
GIV>                } while(--n>0);
GIV>    }
GIV>}
GIV>


GIV>второй день покоя не дает.


Это называется Duff's device (Tom Duff 1983)
http://www.lysator.liu.se/c/duffs-device.html
Re[2]: Зачем может понадобиться такой код?
От: alnsn Великобритания http://nasonov.blogspot.com
Дата: 25.08.03 13:10
Оценка:
GIV>>второй день покоя не дает.

А>Это называется Duff's device (Tom Duff 1983)

А>http://www.lysator.liu.se/c/duffs-device.html

http://www.cuj.com/documents/s=7990/cujcexp1910alexandr/alexandr.htm
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.