2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




На страницу Пред.  1, 2, 3, 4, 5, 6, 7  След.
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение07.11.2014, 11:04 
Аватара пользователя
bin в сообщении #927632 писал(а):
Есть книга Скотт Мейерс, Эффективное использование С++. Там много примеров безумного кода на C++.

Там как раз наиболее разумный.

(Понятие "наиболее разумный код на C++" меняется со временем :-)

-- 07.11.2014 11:14:31 --

Тьфу, я спутал с другими книгами и авторами (Саттер, книга "Эффективное программирование на C++" Кёнига и Му).

У Мейерса, конечно же, много ляпов. Впрочем, в своё время и на своём месте - он сыграл положительную роль.

 
 
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение07.11.2014, 14:36 
Аватара пользователя
EtCetera
Не думал, что окажусь адвокатом Си :-), но полагаю, что cudaMalloc не подходит под опасения из comp.lang.c FAQ Question 4.9. Вот минимальный пример ее использования из книги Сандерс и Кэндрот:
код: [ скачать ] [ спрятать ]
Используется синтаксис C
/*
 * Copyright 1993-2010 NVIDIA Corporation.  All rights reserved.
 ...
 */


#include "../common/book.h"

__device__ int addem( int a, int b ) {
    return a + b;
}

__global__ void add( int a, int b, int *c ) {
    *c = addem( a, b );
}

int main( void ) {
    int c;
    int *dev_c;
    HANDLE_ERROR( cudaMalloc( (void**)&dev_c, sizeof(int) ) );

    add<<<1,1>>>( 2, 7, dev_c );

    HANDLE_ERROR( cudaMemcpy( &c, dev_c, sizeof(int),
                              cudaMemcpyDeviceToHost ) );
    printf( "2 + 7 = %d\n", c );
    HANDLE_ERROR( cudaFree( dev_c ) );

    return 0;
}
Далее авторы отмечают:
Цитата:
Более интересно выделение памяти с помощью функции cudaMalloc(). Она очень похожа на стандартную функцию mallос (), но говорит исполняющей среде CUDA, что память должна быть выделена на устройстве. Первый аргумент - это указатель на указатель, в котором будет возвращен адрес выделенной области памяти, а второй - размер этой области. Если не считать того, что указатель на выделенную область не возвращается функцией в виде значения, то поведение ничем не отличается от malloc(), даже тип возвращаемого значения void* совпадает. Конструкция HANDLE_ERROR(), окружающая обращения, - это служебный макрос, определенный в коде, прилагаемом к книге. Когда функция возвращает ошибку, он печатает сообщение и завершает приложение с кодом EXIT_FAILURE. Хотя никто не запрещает вам использовать этот макрос и в своих программах, скорее всего, такой стратегии обработки ошибок в промышленном коде будет недостаточно.
И тут возникает тонкий, но важный момент. Своей простотой и мощью язык CUDA С во многом обязан стиранию грани между кодом для CPU и для устройства. Однако программист не должен разыменовывать указатель, возвращаемый cudaMalloc(), в коде, исполняемом CPU. Этот указатель можно передавать другим функциям, выполнять с ним арифметические операции и даже приводить к другому типу. Но ни читать, ни писать в эту область памяти нельзя.
К сожалению, компилятор не может защитить от такой ошибки. Он с радостью позволит разыменовать указатель на память устройства в коде, исполняемом CPU, потому что синтаксически этот указатель ничем не отличается от любого другого. (c.35)
Т.о. это функция низкого уровня с большими ограничениями. Как видно из примера, cudaMalloc используется совместно с cudaMemcpy для клонирования переменной с в dev_c на GPU. При столь ограниченном использовании почти невозможно практически получить баг, связанный с отсутствием контроля типов в cudaMalloc. При том, что в ядре add этот контроль производится. Сандерс и Кэндрот используют обертку (book.h):
Используется синтаксис C
static void HandleError( cudaError_t err,
                         const char *file,
                         int line ) {
    if (err != cudaSuccess) {
        printf( "%s in %s at line %d\n", cudaGetErrorString( err ),
                file, line );
        exit( EXIT_FAILURE );
    }
}
#define HANDLE_ERROR( err ) (HandleError( err, __FILE__, __LINE__ ))
 
Можно сгородить более сложную. Но и в защитном программировании не надо доходить до абсурда проверяя и перепроверяя все по несколько раз. Простенький пример из другого языка:
код: [ скачать ] [ спрятать ]
Используется синтаксис Delphi
program pedant;

{$APPTYPE CONSOLE}
uses
  SysUtils;
var
  i,     // счетчик цикла
  n,     // размер последовательности
  num,   // очередное число последовательности
  sum : integer;   // сумма чисел последовательности
  average : real; // среднее арифметическое

function myDiv (x,y : integer; var myResult : real) : Boolean;
// если y<>0, то myResult=x/y и функция возвращает true
// если y=0, то функция возвращает false
begin
  Result := y<>0;
  if Result then // повторная проверка
   myResult := x/y;
end; // myDiv

begin
  writeln ('Среднее арифметическое последовательности целых чисел');
  repeat
    write ('Укажите размер последовательности: ');
    readln (n);
    if n<=0 then
     writeln ('Неправильно указан размер');
  until n>0;
  sum := 0;
  for i:=1 to n do
    begin
      write ('Укажите ',i,'-е число: ');
      readln (num);
      sum := sum + num;
    end;
  if myDiv (sum, n, average) then
   writeln ('Среднее арифметическое ', average)
  else
   writeln ('Неправильно указан размер'); // никогда не исполняется
  write ('Нажмите Ввод для выхода ...');
  readln;
end.
Всегда ли перед делением нужно проверять, что делитель не ноль? Можно подойти еще педантичнее и при вводе проверять на нечисловые символы и т.д. ... и раздуть маленькую тривиальную программку до нетривиальных размеров ;-)

 
 
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение07.11.2014, 22:25 
Munin
Munin в сообщении #927748 писал(а):
У Мейерса, конечно же, много ляпов.
Приведите примеры, пожалуйста. :-)

bin
bin в сообщении #927812 писал(а):
Не думал, что окажусь адвокатом Си :-), но полагаю, что cudaMalloc не подходит под опасения из comp.lang.c FAQ Question 4.9.
1. Вы так быстро переключаетесь с C на C++ и обратно, что я не успеваю за Вами. Это два разных языка, вообще говоря.
2. Вполне себе подходит. Безопасно можно приводить указатели только к void*. void** таким свойством не обладает, поэтому приведение int** $\to$ void** (используемое в примере из книжки) потенциально ведет к undefined behaviour, если sizeof(int**) != sizeof(void**) и/или отличается их бинарное представление. Корректный код в данном случае мог бы выглядеть как-то так:
Используется синтаксис C
void *dev_c_tmp;
HANDLE_ERROR( cudaMalloc( &dev_c_tmp, sizeof(int) ) );
int *dev_c = dev_c_tmp;
bin в сообщении #927812 писал(а):
При столь ограниченном использовании почти невозможно практически получить баг, связанный с отсутствием контроля типов в cudaMalloc.
О каком баге идет речь?

 
 
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение07.11.2014, 22:29 
EtCetera в сообщении #927972 писал(а):
в C++ необходим явный static_cast или C-style cast
Нет, это вы попутали. Это из void* в С приведения не надо, а в C++ надо. К void* в обоих языках приведение неявное.

 
 
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение07.11.2014, 22:38 
warlock66613
warlock66613 в сообщении #927975 писал(а):
Это из void* в С приведения не надо, а в C++ надо. К void* в обоих языках приведение неявное.
Да, разумеется. Но Кристобаль Хозевич успел раньше. $\copyright$

 
 
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение07.11.2014, 22:42 
EtCetera в сообщении #927972 писал(а):
приведение int** $\to$ void** (используемое в примере из книжки) потенциально ведет к undefined behaviour
Ну, это очень-очень потенциально. Во всех значимых реализациях в данном случае имеется implementation-defined behavior, причём во всех имплементациях этот самый behavior одинаковый и прозрачный. Писать же код, поведение которого зависит от реализации, - древняя и священная традиция C-программеров.

 
 
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение07.11.2014, 23:46 
Аватара пользователя
EtCetera в сообщении #927972 писал(а):
1. Вы так быстро переключаетесь с C на C++ и обратно, что я не успеваю за Вами. Это два разных языка, вообще говоря.
Так и я тоже не успеваю за Вами: Вы на какой сайт мне ссылку дали?: на comp.lang.c, а не на comp.lang.cpp... такого нет, а почему? ;-)
EtCetera в сообщении #927972 писал(а):
О каком баге идет речь?
Полагаю, что о том же, о котором говорите Вы:
EtCetera в сообщении #927972 писал(а):
если sizeof(int**) != sizeof(void**)
с cudaMalloc есть единственная возможность сделать баг - указать не тот size. Выше Вы привели пример из CUDA Fast Fourier Transform library (cuFFT), где:
Используется синтаксис C
cudaMalloc((void**)&data1, sizeof(cufftComplex)*NX*NY*NZ);
cudaMalloc((void**)&data2, sizeof(cufftComplex)*NX*NY*NZ);
Я так понял, что Вы опасаетесь, нпр., такого:
Используется синтаксис C
cudaMalloc((void**)&data1, sizeof(cufftComplex)*NX*NY*NZ);
cudaMalloc((void**)&data2, sizeof(cufftComplex)*NX*NZ*NZ);
Ну так можно написать и так:
Используется синтаксис C
        int size = sizeof(cufftComplex)*NX*NY*NZ;
        cudaMalloc((void**)&data1, size);
        cudaMalloc((void**)&data2, size);
Какие проблемы? ;-)

-- Пт ноя 07, 2014 23:58:01 --

PS Хрестоматийный пример:
Используется синтаксис C
while (i=j)
вместо:
Используется синтаксис C
while (i==j)
Почему в C/C++ допустимо while (i=j), я никак понять не могу...

 
 
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение08.11.2014, 00:11 
Аватара пользователя
EtCetera в сообщении #927972 писал(а):
Приведите примеры, пожалуйста. :-)

Это мне его для начала раскопать надо, потом вычитать... Главный пафос Мейерса - не надо писать быдлокод как на Турбо-Паскале. Ну, в эпоху засилья такого быдлокода - оно и было полезно. Но часть конкретных советов оказалась сама неудачной, и в эпоху Саттера-Александреску - не смотрится. Но конкретики не перечислю, извините. Просто я давно оставил Мейерса за спиной, и не обращался к нему.

Мог бы, конечно, потратить на это полвыходных, но есть вещи и поинтереснее, в физическом и астрономическом разделах, например.

 
 
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение08.11.2014, 09:52 
bin
bin в сообщении #928023 писал(а):
EtCetera в сообщении #927972 писал(а):
1. Вы так быстро переключаетесь с C на C++ и обратно, что я не успеваю за Вами. Это два разных языка, вообще говоря.
Так и я тоже не успеваю за Вами: Вы на какой сайт мне ссылку дали?: на comp.lang.c, а не на comp.lang.cpp... такого нет, а почему? ;-)
Ссылка, которую я привел, одинаково справедлива как для C, так и для C++.
bin в сообщении #928023 писал(а):
EtCetera в сообщении #927972 писал(а):
О каком баге идет речь?
Полагаю, что о том же, о котором говорите Вы:
EtCetera в сообщении #927972 писал(а):
если sizeof(int**) != sizeof(void**)
с cudaMalloc есть единственная возможность сделать баг - указать не тот size. Выше Вы привели пример из CUDA Fast Fourier Transform library (cuFFT), где:
Используется синтаксис C
cudaMalloc((void**)&data1, sizeof(cufftComplex)*NX*NY*NZ);
cudaMalloc((void**)&data2, sizeof(cufftComplex)*NX*NY*NZ);
Я так понял, что Вы опасаетесь, нпр., такого:
Используется синтаксис C
cudaMalloc((void**)&data1, sizeof(cufftComplex)*NX*NY*NZ);
cudaMalloc((void**)&data2, sizeof(cufftComplex)*NX*NZ*NZ);
Ну так можно написать и так:
Используется синтаксис C
        int size = sizeof(cufftComplex)*NX*NY*NZ;
        cudaMalloc((void**)&data1, size);
        cudaMalloc((void**)&data2, size);
Какие проблемы? ;-)
Какое отношение имеет неправильное задание размера во втором параметре cudaMalloc к
EtCetera в сообщении #927972 писал(а):
sizeof(int**) != sizeof(void**)
?
Внимательно прочитайте, пожалуйста, текст по вышеприведенной ссылке.

Munin
Munin в сообщении #928031 писал(а):
Но конкретики не перечислю, извините.
Жаль, очень жаль.

 
 
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение08.11.2014, 10:09 
Аватара пользователя
EtCetera в сообщении #928111 писал(а):
Жаль, очень жаль.

Кажется, я второй раз отличился склерозом: возможно, мои воспоминания вообще относятся не к Мейерсу, а к Голубу.

 
 
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение08.11.2014, 11:16 
Аватара пользователя
Да, приношу извинения, про Мейерса это я зря.

 
 
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение08.11.2014, 11:40 
Аватара пользователя
EtCetera в сообщении #928111 писал(а):
Ссылка, которую я привел, одинаково справедлива как для C, так и для C++.
Вот именно! И везде тоже самое. Поэтому C++ воспринимается как расширение С, а не как другой язык. Аналогично Pascal и OO Pascal - это два разных языка?
EtCetera в сообщении #928111 писал(а):
Какое отношение имеет неправильное задание размера во втором параметре cudaMalloc к sizeof(int**) != sizeof(void**)? Внимательно прочитайте, пожалуйста, текст по вышеприведенной ссылке.
Прежде всего, где в CUDA-примерах Вы нашли, чтобы размер в cudaMalloc задавался через sizeof(void**)?

А текст не только прочел, но частично и процитирую:
Цитата:
To appreciate the problem with void ** more clearly, compare the situation to an analogous one involving, say, types int and double, which probably have different sizes and certainly have different representations. If we have a function
void incme(double *p)
{
*p += 1;
}
then we can do something like
int i = 1;
double d = i;
incme(&d);
i = d;
and i will be incremented by 1. (This is analogous to the correct void ** code involving the auxiliary vp.) If, on the other hand, we were to attempt something like
int i = 1;
incme((double *)&i); /* WRONG */
(this code is analogous to the fragment in the question), it would be highly unlikely to work.
Тут функция incme изменяет значение переменной, находящейся по указанной ссылке, а cudaMalloc всего лишь выделяет заказанный объем памяти на устройстве. В этом принципиальная разница. Очевидно, что целое и вещественное представляются в машине по-разному, занимают разный объем памяти, и чтобы увеличить их на единицу, действовать надо по-разному. А вот чтобы выделить память под целое и под вещественное, надо просто знать их размеры. Одни и те же ящики можно использовать для хранения как риса, так и гречки: зная объемы ящика и имеющейся крупы, можно легко определить, сколько нужно ящиков, а какого типа эта крупа (рис, гречка, перловка и т.д.), нас при этом совершенно не интересует.

(BTW Амперсанд в цитатах странно воспроизводится.)

-- Сб ноя 08, 2014 11:52:36 --

BTW мне не ответили:
bin в сообщении #927812 писал(а):
Всегда ли перед делением нужно проверять, что делитель не ноль?

bin в сообщении #928023 писал(а):
Почему в C/C++ допустимо while (i=j)


-- Сб ноя 08, 2014 11:45:29 --

Munin в сообщении #928124 писал(а):
Да, приношу извинения, про Мейерса это я зря.
И про Паскаль зря ;-)

 
 
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение08.11.2014, 11:59 
bin в сообщении #928023 писал(а):
Почему в C/C++ допустимо while (i=j), я никак понять не могу...
А почему оно должно быть недопустимо?

 
 
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение08.11.2014, 12:10 
Аватара пользователя
warlock66613 в сообщении #928132 писал(а):
bin в сообщении #928023 писал(а):
Почему в C/C++ допустимо while (i=j), я никак понять не могу...
А почему оно должно быть недопустимо?
А какой смысл в while (i=j)? Явный баг. В Паскале, нпр,
Используется синтаксис Pascal
while i:=j do

недопустимо.

 
 
 
 Re: Книга с полным и систематичным изложением современного С++?
Сообщение08.11.2014, 12:17 
bin в сообщении #928145 писал(а):
А какой смысл в while (i=j)?
Как вы предлагаете формализовать отсутствие смысла разработчикам компиляторов?

-- 08.11.2014, 13:22 --

warlock66613 в сообщении #928147 писал(а):
Явный баг.
Вот обобщённый пример как "явный баг" используется в реальных программах:
Используется синтаксис C
while(c = getnextchar())
    some_string[i++] = c;

 
 
 [ Сообщений: 101 ]  На страницу Пред.  1, 2, 3, 4, 5, 6, 7  След.


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group