Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Некоторые размышления о пользе и вреде указателей  (Прочитано 23436 раз)
0 Пользователей и 1 Гость смотрят эту тему.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« : 24-04-2013 16:19 » 

Во-первых, в C# есть указатели, только тс-с-с! Об этом лучше не распространяться, особенно среди молодёжи, потому что это будет unsafe код со всеми вытекающими последствиями.

Во-вторых, со времён C известно, что арифметика указателей положительно сказывается на быстродействии. И поэтому в С выражение a[i] есть эквивалент *(a+i). Про C++ и тем более C# такого сказать уже нельзя: оператор [] может быть перегружен и нагружен всяческими дополнительными вещами, как-то проверки диапазонов. Кроме того, в C# объекты под управлением сборщика мусора могут перемещаться по памяти с целью дефрагментации, и решивший вдруг поработать сборщик мусора не оставит шансов добиться быстродействия.

Однако и в C# есть неафишируемые подходы, позволяющие выполнять код без проверок и с простой арифметикой. Но всё равно C/C++ за счёт эффективности компилятора предпочтительнее для интенсивных вычислений, нежели C#.

Итак, общая инфраструктура:
Код: (C#)
using System;
using System.Runtime.InteropServices;

namespace Test
{
    class Program
    {
        delegate void Test();

        const int mx = 10000, my = 10000, md = 1;
        static short[] a = new short[mx * my];
        static double[] b = new double[mx * my];

        unsafe static void Perform(Test test)
        {
            double bestTime = double.MaxValue;
            for (int i = 0; i < 3; ++i)
            {
                DateTime begin = DateTime.Now;
                test();
                DateTime end = DateTime.Now;
                double time = (end - begin).TotalSeconds;
                if(time < bestTime)
                {
                    bestTime = time;
                }
            }
            Console.WriteLine("{0:##.###} s ", bestTime);
        }

        unsafe static void Main(string[] args)
        {
            for (int y = 0; y < my; ++y)
            for (int x = 0; x < mx; ++x)
            {
                a[y * mx + x] = 1;
            }
            Perform(Test1);
            Perform(Test2);
            Perform(Test3);
            Perform(Test4);
            Console.ReadKey();
        }
    }
}
Код: (C++)
#include <climits>
#include <iostream>
#include <Windows.h>

using namespace std;

typedef void (*Test)();

const int mx = 10000, my = 10000, md = 1;
short *a;
double *b;

void Perform(Test test)
{
    double bestTime = DBL_MAX;
    for (int i = 0; i < 3; ++i)
    {
        DWORD begin = GetTickCount();
        test();
        DWORD end = GetTickCount();
        double time = (end - begin) / 1000.0;
        if(time < bestTime)
        {
            bestTime = time;
        }
    }
    std::cout << bestTime << " s " << std::endl;;
}

int main()
{
    a = new short[mx * my];
    b = new double[mx * my];
    for (int y = 0; y < my; ++y)
    for (int x = 0; x < mx; ++x)
    {
        a[y * mx + x] = 1;
    }
    Perform(Test1);
    Perform(Test2);
    //Perform(Test3);
    Perform(Test4);
    delete[] a;
    delete[] b;
    cin.get();
    return 0;
}

Теперь сравниваем алгоритм, задача которого в массиве b записать усреднения 3х3 областей из a в обратном порядке. Замеры времени работы производились на одной и той же машине, платформа x86. C++ и C# из VS 2012, .NET 4.5, оптимизации включены в обоих случаях, сборка Release.

Первый тест: решение в лоб
Код: (C#)
        static void Test1()
        {
            for (int y = md; y < my - md; ++y)
            for (int x = md; x < mx - md; ++x)
            {
                b[(my - y - 1) * mx + (mx - x - 1)] = 0.0;
                for (int dy = -md; dy <= md; ++dy)
                for (int dx = -md; dx <= md; ++dx)
                {
                    b[(my - y - 1) * mx + (mx - x - 1)] += a[(y + dy) * mx + (x + dx)];
                }
                b[(my - y - 1) * mx + (mx - x - 1)] /= (md * 2 + 1) * (md * 2 + 1);
            }
        }
 
Работает 9.7 секунды.
Код: (C++)
void Test1()
{
    for (int y = md; y < my - md; ++y)
    for (int x = md; x < mx - md; ++x)
    {
        b[(my - y - 1) * mx + (mx - x - 1)] = 0.0;
        for (int dy = -md; dy <= md; ++dy)
        for (int dx = -md; dx <= md; ++dx)
        {
            b[(my - y - 1) * mx + (mx - x - 1)] += a[(y + dy) * mx + (x + dx)];
        }
        b[(my - y - 1) * mx + (mx - x - 1)] /= (md * 2 + 1) * (md * 2 + 1);
    }
}
 
Работает 1.3 секунды.

Второй тест: попытка сократить вычисления, вручную вытаскивая из циклов повторяющиеся расчёты и устраняя выделение/удаление переменных на стеке.
Код: (C#)
        static void Test2()
        {
            int x, y, dx, dy, oa, ob, pb, oda, myl, mxl, s;
            myl = my - md;
            mxl = mx - md;
            s = md * 2 + 1;
            s *= s;
            for (y = md; y < myl; ++y)
            {
                oa = y * mx;
                ob = (my - y - 1) * mx;
                for (x = md; x < mxl; ++x)
                {
                    pb = ob + (mx - x - 1);
                    b[pb] = 0.0;
                    for (dy = -md; dy <= md; ++dy)
                    {
                        oda = oa + dy * mx;
                        for (dx = -md; dx <= md; ++dx)
                        {
                            b[pb] += a[oda + (x + dx)];
                        }
                    }
                    b[pb] /= s;
                }
            }
        }
Работает 8.9 секунды.
Код: (C++)
void Test2()
{
    int x, y, dx, dy, oa, ob, pb, oda, myl, mxl, s;
    myl = my - md;
    mxl = mx - md;
    s = md * 2 + 1;
    s *= s;
    for (y = md; y < myl; ++y)
    {
        oa = y * mx;
        ob = (my - y - 1) * mx;
        for (x = md; x < mxl; ++x)
        {
            pb = ob + (mx - x - 1);
            b[pb] = 0.0;
            for (dy = -md; dy <= md; ++dy)
            {
                oda = oa + dy * mx;
                for (dx = -md; dx <= md; ++dx)
                {
                    b[pb] += a[oda + (x + dx)];
                }
            }
            b[pb] /= s;
        }
    }
}
Работает 1.3 секунды.

Компилятор C++ все эти усовершенствования игнорирует - и правильно делает. Комплиятор C# тоже практически игнорирует, хотя 5% выиграли.

В C# можно особой директивой отключить разные проверки переполнения и т.п. В C++ аналога нет, поэтому третий тест лишь в одном варианте:
Код: (C#)
        static void Test3()
        {
            unchecked
            {
                int x, y, dx, dy, oa, ob, pb, oda, myl, mxl, s;
                myl = my - md;
                mxl = mx - md;
                s = md * 2 + 1;
                s *= s;
                for (y = md; y < myl; ++y)
                {
                    oa = y * mx;
                    ob = (my - y - 1) * mx;
                    for (x = md; x < mxl; ++x)
                    {
                        pb = ob + (mx - x - 1);
                        b[pb] = 0.0;
                        for (dy = -md; dy <= md; ++dy)
                        {
                            oda = oa + dy * mx;
                            for (dx = -md; dx <= md; ++dx)
                            {
                                b[pb] += a[oda + (x + dx)];
                            }
                        }
                        b[pb] /= s;
                    }
                }
            }
        }
Работает 9.2 секунды, т.е. разницы с предыдущим тестом фактически нет, а может даже и стало хуже.

Наконец, последний тест без оператора [] с арифметикой указателей.
Код: (C#)
        unsafe static void Test4()
        {
            unchecked
            {
                GCHandle ha = GCHandle.Alloc(a, GCHandleType.Pinned);
                GCHandle hb = GCHandle.Alloc(b, GCHandleType.Pinned);
                short* pa = (short*)ha.AddrOfPinnedObject();
                double* pb = (double*)hb.AddrOfPinnedObject();

                int x1 = md, xn = (mx - 1) - md;
                int y1 = md * mx, yn = (my - 1 - md) * mx;
                int dx1 = -md, dxn = md;
                int dy1 = -md * mx, dyn = md * mx;

                short* pya, pan, pxya, pyan, pdy, pdn, pdxy, pdyn;
                double* pyb, pxyb;
                int s;
                s = md * 2 + 1;
                s *= s;

                for (pya = pa + y1, pan = pa + yn, pyb = pb + yn; pya <= pan; pya += mx, pyb -= mx)
                for (pxya = pya + x1, pyan = pya + xn, pxyb = pyb + xn; pxya <= pyan; ++pxya, --pxyb)
                {
                    *pxyb = 0.0;
                    for (pdy = pxya + dy1, pdn = pxya + dyn; pdy <= pdn; pdy += mx)
                    for (pdxy = pdy + dx1, pdyn = pdy + dxn; pdxy <= pdyn; ++pdxy)
                    {
                        *pxyb += *pdxy;
                    }
                    *pxyb /= s;
                }

                ha.Free();
                hb.Free();
            }
        }
Работает 6.9 секунды.
Код: (C++)
void Test4()
{
    short *pa = a;
    double *pb = b;

    int x1 = md, xn = (mx - 1) - md;
    int y1 = md * mx, yn = (my - 1 - md) * mx;
    int dx1 = -md, dxn = md;
    int dy1 = -md * mx, dyn = md * mx;

    short *pya, *pan, *pxya, *pyan, *pdy, *pdn, *pdxy, *pdyn;
    double *pyb, *pxyb;
    int s;
    s = md * 2 + 1;
    s *= s;

    for (pya = pa + y1, pan = pa + yn, pyb = pb + yn; pya <= pan; pya += mx, pyb -= mx)
    for (pxya = pya + x1, pyan = pya + xn, pxyb = pyb + xn; pxya <= pyan; ++pxya, --pxyb)
    {
        *pxyb = 0.0;
        for (pdy = pxya + dy1, pdn = pxya + dyn; pdy <= pdn; pdy += mx)
        for (pdxy = pdy + dx1, pdyn = pdy + dxn; pdxy <= pdyn; ++pdxy)
        {
            *pxyb += *pdxy;
        }
        *pxyb /= s;
    }
}
Работает 4.5 секунды.

Из чего следует, что автоматическую оптимизацию компилятора C++ мы своей ручной безнадёжно сломали, а вот для C# получили заметное ускорение. Причём несложным экспериментом можно убедиться, что оно зависит исключительно от неиспользования оператора [], а не от защиты массивов от активности сборщика мусора.

Любопытно также, что Debug сборка C++ и Release сборка C# работают одинаково по времени. Ну это лишь совпадение.

Также стоит отметить, что при объявлении указателей звёздочка * в C# - элемент имени типа, а в C++ - элемент декларации переменной. На это можно напороться, если объявлять много переменных в одной строке через запятую - см. выше.

Мораль:
- Если нужны интенсивные вычисления с беганием по большим и не влезающим в кэш массивам (например, при обработке видеопотоков), лучше их писать на C++.
- Если в подобных задачах от C# отказаться никак, то ручная оптимизация за счёт выноса повторно используемых значений во внешние циклы и использование указателей, как показано выше, способно дать некоторое ускорение примерно на 20-25%.
« Последнее редактирование: 24-04-2013 18:17 от Алексей++ » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #1 : 24-04-2013 18:17 » new

подправил - вставил в одном месте nobbc , а то кривилось
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #2 : 24-04-2013 20:16 » 

Есть синтаксический сахар. Вместо
Код: (C#)
GCHandle h = GCHandle.Alloc(a, GCHandleType.Pinned);
int* p = h.AddrOfPinnedObject();
// ...
h.Free();
можно использовать
Код: (C#)
fixed(int* p = a)
{
  // ...
}
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dmitry
Помогающий

ru
Offline Offline

« Ответ #3 : 25-04-2013 14:35 » 

Dimka, я как-то тоже проводил эксперименты на предмет сравнения С++ и С#, и получалось, что небезопасный код практически не уступает в скорости плюсам.
Ты с отладчиком код запускал? Тогда у тебя JIT компилятор без оптимизаций работал.

У меня так.
C#: 4.95, 4.805, 4.8, 2.9
C++: 1.08, 1.09, 3.026
Но всё равно медленно Улыбаюсь
Тесты 2 и 3 для шарпа одинаковые - компилятор по-умолчанию генерирует команды без проверки на переполнение.
« Последнее редактирование: 25-04-2013 14:51 от Dmitry » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #4 : 25-04-2013 19:17 » 

Dmitry, а, да, не обратил внимание. Ну минус 3 секунды по всем пунктам для C#.
« Последнее редактирование: 25-04-2013 19:19 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dmitry
Помогающий

ru
Offline Offline

« Ответ #5 : 25-04-2013 19:28 » 

Dimka, а я поигрался с атрибутом DebuggableAttribute. Падение скорости даёт не отсутствие JIT оптимизации (она на этом примере практически ничего не даёт), а включенный JITTracking, который позволяет работать отладчику.
Записан
Dmitry
Помогающий

ru
Offline Offline

« Ответ #6 : 26-04-2013 09:25 » 

Заводим локальную переменную и избавляемся от постоянной записи промежуточных вычислений через указатель в кучу. Плюс становится меньше преобразований к double.
Код: (C#)
private static unsafe void Test5()
{
    fixed (short* pa = a)
    {
        fixed (double* pb = b)
        {
            const int x1 = md, xn = (mx - 1) - md;
            const int y1 = md*mx, yn = (my - 1 - md)*mx;
            const int dx1 = -md, dxn = md;
            const int dy1 = -md*mx, dyn = md*mx;

            short* pya, pan, pxya, pyan, pdy, pdn, pdxy, pdyn;
            double* pyb, pxyb;
            const int s = (md*2 + 1)*(md*2 + 1);

            for (pya = pa + y1, pan = pa + yn, pyb = pb + yn; pya <= pan; pya += mx, pyb -= mx)
                for (pxya = pya + x1, pyan = pya + xn, pxyb = pyb + xn; pxya <= pyan; ++pxya, --pxyb)
                {
                    *pxyb = 0.0;
                    int sum = 0;
                    for (pdy = pxya + dy1, pdn = pxya + dyn; pdy <= pdn; pdy += mx)
                        for (pdxy = pdy + dx1, pdyn = pdy + dxn; pdxy <= pdyn; ++pdxy)
                        {
                            sum += *pdxy;
                        }
                    *pxyb = (double)sum / s;
                }
        }
    }
}
 
1.43 s

Разворачиваем внутренний цикл (вдруг компилятор С++ тоже так умеет)
Код: (C#)
private static unsafe void Test6()
{
    fixed (short* pa = a)
    {
        fixed (double* pb = b)
        {
            const int x1 = md, xn = (mx - 1) - md;
            const int y1 = md*mx, yn = (my - 1 - md)*mx;
            const int dy1 = -md*mx, dyn = md*mx;

            short* pya, pan, pxya, pyan, pdy, pdn;
            double* pyb, pxyb;
            const int s = (md*2 + 1)*(md*2 + 1);

            for (pya = pa + y1, pan = pa + yn, pyb = pb + yn; pya <= pan; pya += mx, pyb -= mx)
                for (pxya = pya + x1, pyan = pya + xn, pxyb = pyb + xn; pxya <= pyan; ++pxya, --pxyb)
                {
                    *pxyb = 0.0;
                    int sum = 0;
                    for (pdy = pxya + dy1, pdn = pxya + dyn; pdy <= pdn; pdy += mx)
                    {
                        sum += *(pdy - 1);
                        sum += *(pdy);
                        sum += *(pdy + 1);
                    }
                    *pxyb = (double)sum / s;
                }
        }
    }
}
0.81s

upd

Что-то разница между двумя этими временами слишком большая...

Проверил - С++ цикл не разворачивает. Если развернуть в первом тесте, время будет 0.86 (было 1.08). Т.е. тут С# не уступает, а может даже чуть опережает С++.
Теперь забудем про циклы и вспомним, что CLR работает только с 32 или 64 битными значениями. Т.е. в массиве наш short занимает 16 бит, а при копировании в стек дополняется до 32. Поэтому, например, чтобы присвоить результат суммирования двух short переменной типа short, его (результат) надо явно преобразовать обратно в short. В общем, чутьё не подвело. Заменяем массив A на массив int'ов и сбрасываем 0.6с с тестов 1-4.
Для теста 5 получаем 1.12 секунд. Памяти расходуем больше, зато практически догнали C++ (1.08с).
А для 6 как было, так и осталось - 0.81 секунд. Можно теперь в ассемблер заглянуть, что же там происходит  Улыбаюсь

C++ для первого теста пофиг, int массив или short. Но если развернуть цикл (для int[]), получается 0.81 с, как и у C#.
« Последнее редактирование: 26-04-2013 12:30 от Dmitry » Записан
Dmitry
Помогающий

ru
Offline Offline

« Ответ #7 : 26-04-2013 19:24 » 

Оказывается, fixed это не совсем то же самое, что и получение указателя через GCHandle. И p[x+y] не совсем то же самое что *(p+x+y) - компилятор их немного по-разному обрабатывает. Первое, это как *(p+(x+y)).
И вся эта арифметика с указателями скорости не добавляет: появляются лишние умножения и IL команды типа conv_i.
Код: (C#)
private static unsafe void Test()
{
    int s = (md*2 + 1)*(md*2 + 1);
    GCHandle ha = GCHandle.Alloc(a, GCHandleType.Pinned);
    GCHandle hb = GCHandle.Alloc(b, GCHandleType.Pinned);
    short* pa = (short*) ha.AddrOfPinnedObject();
    double* pb = (double*) hb.AddrOfPinnedObject();

    for (int y = md; y < my - md; ++y)
    {
        int mx_y = mx*y;
        int by = (my - y - 1)*mx;
        for (int x = md; x < mx - md; ++x)
        {
            int bxy = by + (mx - x - 1);
            int sum = 0;
            for (int dy = -md; dy <= md; ++dy)
            {
                int ady_x = mx_y + dy*mx + x;
                for (int dx = -md; dx <= md; ++dx)
                {
                    sum += pa[ady_x + dx];
                }
            }
            pb[bxy] = (double) sum/s;
        }
    }

    ha.Free();
    hb.Free();
}
1.0 s

Код: (C++)
void Test()
{
    int  s = md * 2 + 1;
    s *= s;

    for (int y = md; y < my - md; ++y)
    for (int x = md; x < mx - md; ++x)
    {          
        int xy = (my - y - 1) * mx + (mx - x - 1);      
        int sum = 0;
        for (int dy = -md; dy <= md; ++dy)    
        for (int dx = -md; dx <= md; ++dx)
        {                      
            sum += a[(y + dy) * mx + (x + dx)];
                       
        }
        b[xy] = (double)sum / s;
    }
}
0.85 s

Если массив A сделать Int32[], то времена соответственно будут 0.9 и 0.8.
По-моему, сравнение адекватное, и какого-то подавляющего преимущества в скорости нету.
« Последнее редактирование: 26-04-2013 19:57 от Dmitry » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 27-04-2013 09:38 » 

Dmitry, может быть, но в моих задачах есть только массивы short, ushort и float. И их реально много - до террабайта. Так что по частям по мере влезания в доступную память.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dmitry
Помогающий

ru
Offline Offline

« Ответ #9 : 27-04-2013 12:01 » 

Dimka, сравнение и код выше -  это для исходного массива short.
Переменную s лучше объявить как double - лишнее преобразование, IL код на одну команду будет короче. Хотя вряд ли это будет заметно.
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 29-04-2013 14:50 » 

Одно плохо, нельзя заставить, чтобы параметр generic-типа стал основой для указателя. Т.е. создать generic-класс для работы с указателем в C# не получится.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #11 : 29-04-2013 15:33 » 

Ну что ж, раз нельзя написать что-то в духе:
Код: (C#)
unsafe class C<E> where E : struct
{
  E* Get()
  {
    // ...
  }
}
Поскольку при компиляции обнаруживается, что тип E неизвестный, может быть даже managed, sizeof для него непонятный, и никак это не собирается.

Приходится оперировать void*. Но для void* (в отличие от C++) арифметики указателей нет - и это наверно правильно. Хочешь арифметику, используй byte*. Тогда можно соорудить нечто вроде:
Код: (C#)
unsafe void* Read(void* buffer, int elementSize, int offset)
{
  return (byte*)buffer + elementSize * offset;
}

// ...

int* buffer;
// ...
int* p = Read(buffer, sizeof(int), 123);

Но, конечно, все такие вещи стоят драгоценных тактов.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dmitry
Помогающий

ru
Offline Offline

« Ответ #12 : 29-04-2013 16:54 » 

Так а в чём смысл этого? Тип указателя и определяет elementSize, используемый для арифметики с указателями, только обычно это делает компилятор, а тут ты делаешь это явно. Это полностью эквивалентно int* p = buffer + 123, в т.ч. и по скорости (если вызов метода будет встроен). Или там может быть какая-то универсальная логика?
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #13 : 29-04-2013 19:29 » 

Dmitry, я тебя не понял. В чём смысл void* или в чём смысл generic-параметра?
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dmitry
Помогающий

ru
Offline Offline

« Ответ #14 : 29-04-2013 21:18 » 

Того кода c void* и арифметикой указателей, если компилятор то же самое делает сам, для указателя на конкретный тип.
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #15 : 29-04-2013 21:54 » 

Dmitry, зачем мне это надо, я тут нигде и не распространяюсь. Разумеется, лишние преобразования без причины не делаются.

Здесь я лишь отметил, что невозможность использовать generic-типы вынуждает прибегать к байтовым указателям и дополнительным расчётам.

P.S. Разница между sizeof(int) и переменной int size есть: первая - константа, и умножение на неё может быть оптимизировано, вторая - нет, и это не оптимизируется, поскольку значение непредсказуемо.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dmitry
Помогающий

ru
Offline Offline

« Ответ #16 : 29-04-2013 22:44 » 

Так почему бы не делать расчёты для индексов элементов? В итоге всё равно ведь будут использоваться конкретные типы, так что компилятор сам сгенерирует код для расчёта смещения адресов.

P.S. ок, это эквивалентно int* p = buffer + int
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #17 : 29-04-2013 23:26 » 

Цитата: Dmitry
Так почему бы не делать расчёты для индексов элементов? В итоге всё равно ведь будут использоваться конкретные типы, так что компилятор сам сгенерирует код для расчёта смещения адресов.
Продолжаю не понимать. Где будут использованы конкретные типы? Внутри Read типы совсем не те, что снаружи. И добиться единства при помощи generic не выйдет - это не C++.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dmitry
Помогающий

ru
Offline Offline

« Ответ #18 : 30-04-2013 00:14 » 

Снаружи. Чтобы разыменовать указатель, нам же нужно знать конкретный тип.
Код: (C#)
int* buffer1;
int* p1 = (int*)Read(buffer1, sizeof(int), 123);

A* buffer2;
A* p2 = (A*)Read(buffer2, sizeof(A), 123);

Равносильно
Код: (C#)
int* buffer1;
int* p1 = buffer1 + 123;

A* buffer2;
A* p2 = buffer2 + 123;
только в последнем случае компилятор сразу встроит в IL код значение sizof*123. Если использовать переменную, разницы никакой.
В обоих вариантах значение p зависит от типа указателя. Вот я и понять не могу, зачем нужен Read? Или это вообще не рабочий код, и обсуждение не имеет смысла? Улыбаюсь
« Последнее редактирование: 30-04-2013 00:21 от Dmitry » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #19 : 30-04-2013 08:44 » 

Dmitry, кому нужен? Мне - нужен. Тебе не нужен - не используй. В чём проблема-то?

P.S. И что такое A*? Либо A - alias одного из числовых типов, либо такой код даже не скомпилируется.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dmitry
Помогающий

ru
Offline Offline

« Ответ #20 : 30-04-2013 09:27 » 

Dimka, ок, никаких проблем
A - любой unmanaged тип.
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #21 : 30-04-2013 10:10 » 

Цитата: Dmitry
A - любой unmanaged тип.
Это как-то бессодержательно. Параметром generic-типа это быть не может. Следовательно, прямо в коде такое не написать. Либо использовать alias в пределах одного файла кода, либо макроподстановку define глобально. Ни то, ни другое мне не подходит.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dmitry
Помогающий

ru
Offline Offline

« Ответ #22 : 30-04-2013 12:07 » 

Самый общий ответ. Другими словами, это любой не ссылочный тип, не содержащий поля ссылочного типа. На параметр-тип нет возможности наложить такое ограничение.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #23 : 30-04-2013 14:52 » 

а можно глупый вопрос ? ))

вдруг в супер-пупер шарпе понадобились всякие костыли!  Удивительно, на первый взгляд

Кстати, может проще обратиться к DLL, в которой будет код из C++ ?
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #24 : 30-04-2013 15:02 » 

Алексей++, дак так и будет по алгоритмам (см. соседнюю тему про увязку языков), а вот обвязку управления разными контейнерами, потоками, буферами - это безопаснее делать на C# именно из-за лучшего контроля типов, менее громоздкого кода и безопасного управления памятью. Native C++ будет получать только указатели на буферы.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines